A。基本技术题Activity生命周期 第一个Activity01:onCreate01onStart01onResume01 显示dialog,生命周期不会变化,不会调用onPause 跳转到Activity02 a。如果Activity02正常的: onPause01onCreate02onStart02onResume02onStop01onSaveInstanceState01 Activity02退出返回: onPause02onRestart01onStart01onResume01onStop02onDestroy02 b。如果Activity02是透明的 onPause01onCreate02onStart02onResume02 透明Activity02退出返回: onPause02onResume01onStop02onDestroy02 总结:跳转到另一个Activity,首先是当前ActivityonPause,然后新的Activity生命周期一直到onResume,然后之前的Activity再执行onStoponSaveInstanceState。 在adb中使用:adbshellamkill包名(如com。example。lcp)可模拟内存不足时进程被杀死,之后返回该应用时,每个Activity调用的是onCreateonStartonRestoreInstanceStateonResume 2。onNewIntent调用时机、四种启动方式 注:onNewIntent(肯定是已存在,第二次startActivity的时候调用出现的)总是在onResume之前,如果有onStart,就在onStart之后 Standard:每次创建新的 SingleTask:如果栈中已存在,就把它上面的所有实例finish,把它提到栈顶 SingleInstance:只有一个实例,并且运行在自己的独立栈中 SingleTop:如果当前栈顶是它,就复用 自己跳转自己,除了Standard都会调用onNewIntent。 其他跳转过来,SingleTask和SingleInstance会调用onNewIntent,如果当前栈顶是自己,那么SingleTop也会调用onNewIntent singleInstance的坑借鉴这篇文章 这个模式才是重点,也是比较容易入坑的一种启动模式。字面上理解为单一实力。它具备所有singleTask的特点,唯一不同的是,它是存在于另一个任务栈中。上面的三种模式都存在于同一个任务栈中,而这种模式则是存在于另一个任务栈中。举个例子,上面的启动模式都存在于地球上,而这种模式存在于火星上。整个Android系统就是个宇宙。下面来详细介绍一下singleInstance的坑。singleInstance之一坑 此时有三个activity,ActivityA,ActivityB,ActivityC,除了ActivityB的启动模式为singleInstance,其他的启动模式都为默认的。startActivity了一个ActivityA,在ActivityA里startActivity了一个ActivityB,在ActivityB里startActivity了一个ActivityC。 此时在当前的任务栈中的顺序是,ActivityAActivityBActivityC。照理来说在当前ActivityC页面按返回键,finish当前界面后应当回到ActivityB界面。但是事与愿违,奇迹出现了,页面直接回到了ActivityA。 这是为什么呢?其实想想就能明白了,上面已经说过,singleInstance模式是存在于另一个任务栈中的。也就是说ActivityA和ActivityC是处于同一个任务栈中的,ActivityB则是存在另个栈中。所以当关闭了ActivityC的时候,它自然就会去找当前任务栈存在的activity。当前的activity都关闭了之后,才会去找另一个任务栈中的activity。 也就是说当在ActivityC中finish之后,会回到ActivityA的界面,在ActivityA里finish之后会回到ActivityB界面。如果还想回到ActivityB的页面怎么办呢?我的做法是,在ActivityB定义一个全局变量,publicstaticbooleanreturnActivityB;界面需要跳转的时候将returnActivityB然后在ActivityA界面onstart方法里判断returnActivityB是否为true,是的话就跳转到ActivityB,同时将returnActivityB这样就能解决跳转的问题了。不过感觉还不是很好,如果有更好的方法,欢迎大家给我留言告诉我一声。singleInstance之二坑 此时有两个个activity,ActivityA,ActivityB,ActivityA的启动模式为默认的,ActivityB的启动模式为singleInstance。当在ActivityA里startActivity了ActivityB,当前页面为ActivityB。按下home键。应用退到后台。此时再点击桌面图标进入APP,按照天理来说,此时的界面应该是ActivityB,可是奇迹又出现了,当前显示的界面是ActivityA,并且返回不会显示ActivityB了。(如果是在最近运行的应用列表中选择,则回去的是ActivityB,并且返回不会再显示ActivityA了) 这是因为当重新启动的时候,系统会先去找主栈(我是这么叫的)里的activity,也就是APP中LAUNCHER的activity所处在的栈。查看是否有存在的activity。没有的话则会重新启动LAUNCHER。要解决这个方法则是和一坑的解决办法一样,在ActivityB定义一个全局变量,publicstaticbooleanreturnActivityB;在oncreat方法将returnActivityB然后在ActivityA界面onstart方法里判断returnActivityB是否为true,是的话就跳转到ActivityB,同时将returnActivityB这样就能解决跳转的问题了。 注意:Intent设置的Flags优先级高于manifest中设置的启动模式,即运行时的实际要求优先级高于配置 3。HashMap结构、扩容时机、ConcurrentHashMap实现线程安全机制 A。HashMap:数组链表,当链表数量大于8时,链表变成红黑树 B。HahMap扩容时机:初始数组也就是桶的容量capacity是16,必须是2的幂,当map数量等于数组数量capcityfactor(默认值是0。75)时,比如163412时,数组扩容,减小哈希碰撞 C。ConcurrentHashMap使用Synchronized同步块而不是同步方法(1。8,在1。7中使用ReentrantLock重入锁)实现安全机制,同时使用CAS(CompareAndSwap:比较并替换,是UnSafe类,适用于原子类,就是所有操作都是原子的,要么成功,要么失败)操作什么是unsafe呢?Java语言不像C,C那样可以直接访问底层操作系统,但是JVM为我们提供了一个后门,这个后门就是unsafe。unsafe为我们提供了硬件级别的原子操作。至于valueOffset对象,是通过unsafe。objectFiledOffset方法得到,所代表的是AtomicInteger对象value成员变量在内存中的偏移量。我们可以简单的把valueOffset理解为value变量的内存地址。我们上面说过,CAS机制中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。而unsafe的compareAndSwapInt方法的参数包括了这三个基本元素:valueOffset参数代表了V,expect参数代表了A,update参数代表了B。正是unsafe的compareAndSwapInt方法保证了Compare和Swap操作之间的原子性操作。 4。Okhttp和retrofit比volley好在哪里,retrofit实现原理,断点下载 添加配置,retrofit实现restApi,怎么实现的? 断点下载,请求头中有Range数据的信息,只要客户端告诉服务端从range字节开始返回数据就可以了。多线程下载是说每个线程下载不同的range就可以实现了。 5。Binder底层实现机制 6。消息机制、进程间通信B。Kotlin问题: inlinenoinlinecrossinline三个关键字区别,好处是什么 参考StefanJi关于inlinenoinlinecrossinline的解释: a。inline:内联函数,就是在编译期将函数代码直接复制到调用的地方 noinline和crossinline都用在inline函数中 b。noinline就是不要直接复制代码 c。crossinline不允许lambda中有显示的return语句,正常的function可以有C。算法题: 1。链表反转 方法1:对我来说最好的理解方式是:将第一个元素作为轴心current,每次循环都把current的后一个元素放到第一位ClassNode(varnumber:Int?,varnext:Node)funreverse(head:Node){把第一个元素作为当前元素current,每次循环都将current的下一个元素current。next移到第一位,直到current。nextnull为止时间复杂度为O(n),空间复杂度是1varcurrenthead。nextwhile(current?。next!null){valfirsthead。nextvalcurrentNextcurrent。nextcurrent的下一个元素移动到第一位head。nextcurrentNextcurrent。nextcurrentNext?。nextcurrentNext?。nextfirst}} 方法2:funreverse2(head:Node){新建一个节点,每次通过将p节点连接到newFirst,然后newFirstp,pp。next就可以进行链表反转了varnewFirst:Node?nullvarp:Node?head。nextwhile(p!null){valtmpp。nextp。nextnewFirstnewFirstpptmp}head。nextnewHead} 2。一个数组,将所有0前移,非0保持前后位置funarrayMoveZeroToFront(array:IntArray){最后一个0的位置varlastZeroIndex1循环时与最后一个0的位置对比,如果非0并且后面还有0,就交换位置for(iinarray。indices。reversed()){if(array〔i〕0){if(lastZeroIndexi)lastZeroIndexi}else{if(ilastZeroIndex){array〔lastZeroIndex〕array〔i〕array〔i〕0lastZeroIndex}}}println(array。contentToString())}