面试问题总结之Android篇
handler相关
根据源码里的文档,handler的主要作用有两个:(1)将message和runnable安排在未来的某个时间点执行;(2)将一个操作提交到另一个线程的队列中执行。
handler的原理一直是个麻烦问题,面试经常会问到,而且总是说不好。我很想把他总结出来,每次面试都可以用同一套内容来回答。以下是我的尝试。
handler会关联一个looper,looper和messageQueue是一一对应关系。handler可以通过post()和sendMessage()(及各种Delayed、AtTime系列方法)发送消息至messageQueue;looper一直循环从messageQueue里取出message;每个message有一个target,就是发出该message的那个handler,当looper取出message的时候,执行一句:
msg.target.dispatchMessage(msg)
dispatchMessage(msg)实际是调用了target handler里的handleMessage()方法,将这个message给回target handler进行处理。于是最终的处理是在创建该handler的线程中执行的。
举例:更新UI。主线程里创建一个handler实例,将引用传给子线程,子线程完成耗时操作后,使用handler将消息发送至主线程的messageQueue,最后在主线程中执行UI的更新操作。
生命周期相关
生命周期应该是很基础的内容了,但是有些细节我依然很模糊。
onPause(),onStop()
源码里的文档说activity只要转入后台就会调用onPause(),在不可见时才会调用onStop()。一般来说onPause()调用之后都会跟着一个onStop()的调用,但是也有的情况是onPause()之后直接到onResume()了。onPause()被执行的可能性是最大的(进程被杀死的时候可能不会调用onStop()和onDestroy()),所以在这个方法里可以进行一些状态保存的操作。
onSaveInstanceState()
状态保存的官方方法,文档里是这么说的:在activity被杀死以回收系统资源的时候这个方法会被调用,可以保存一些状态用作下次onCreate()或者onRestoreInstanceState()的时候恢复。例如activity A上新开了个activity B,A转到后台,由于要回收系统资源而把A杀死了,那么这个方法会被调用。
文档里还说,不要把这个方法和生命周期里的onPause(), onStop()弄混,有两种情况这个方法不会调用:例如刚才的例子,从B退回到A时,B不会调用这个方法,因为系统认为B的状态不需要保存;或者是B在A前面时,A一直没被杀死,A也不会调用这个方法,因为A的状态一直就在那没变。
onNewIntent()
文档只提到activity在把启动模式设成singleTop,或者intent里面带了FLAG_ACTIVITY_SINGLE_TOP的时候,重新启动activity不会新建一个实例而是调用当前实例的onNewIntent()。但是我感觉启动模式是singleTask或者singleInstance的时候也应该是这样的,网上的一些资料也这么说。
onConfigurationChanged()
设备的设置改变时会触发,如果manifest里设置了android:configChanges属性,才会调用这个方法,默认情况是会重启activity的,也就是得要重新调用onCreate()了。
PS:API13以上在设置了权限android.permission.CHANGE_CONFIGURATION和android:configChanges="orientation"之后,还是有可能不调用onConfigurationChanged(),需要在android:configChanges里面加一个"screenSize"。
singleTask 和 singleInstance
这两个的区别也是一直搞不清楚的问题,综合了文档和一些资料,总结一下:
首先在manifest的activity标签下还可以设置一项属性叫做taskAffinity,用来标识activity属于哪个task。默认情况下taskAffinity是应用的包名。
设置了singleTask的activity,如果没有设置taskAffinity,那么启动时还是会在原本的task当中,因为他会去找跟它的taskAffinity相同的task,如果存在这样一个task,就在这个task中启动,如果不存在这样一个task,才创建一个新的task,并在新task中启动。如果已经存在这个activity的实例,那么会调用onNewIntent()。
singleInstance和singleTask只有一点不一样,设置了singleInstance的activity启动时必然会在新的task当中,而且这个task只会有这一个activity存在。没设置taskAffinity的话,在他之上新建的activity会回到之前的task当中,设置了taskAffinity的话,新建的activity会再另起一个task。
这部分参考了http://blog.csdn.net/linmiansheng/article/details/24297375
点击事件的传递
故事开始于activity的dispatchTouchEvent()方法,activity收到触摸事件以后调用这个方法进行处理,把事件向下分发给各级view。在activity的这个方法中,先会尝试调用window.superDispatchKeyShortcutEvent()将事件向下分发。如果下面的view有人处理了该事件,则返回true;如果都不处理,则会调用自己的onTouchEvent()方法,返回该方法调用结果。
继续往下,一般来说下面应该是一个layout了,也就是ViewGroup。ViewGroup里的dispatchTouchEvent()源码特别长,大概内容是调用了自己的onInterceptTouchEvent()方法,查询是否要拦截事件(调用之前还先查了FLAG_DISALLOW_INTERCEPT看是不是允许拦截),根据是否拦截进行不同处理:
- 如果不拦截,继续往下分发;
- 如果拦截,那么由自己来处理,进入处理流程:首先看自己有没有设置onTouchListener,有的话调用之,没有的话则轮到自己的
onTouchEvent()方法,其中还会再看有没有设置onClickListener,有的话调用之。
如果再往下分发到一个view当中了,view是没有onInterceptTouchEvent()的,他的dispatchTouchEvent()只会分配给自己处理,处理过程和ViewGroup的处理流程一样。
如果ViewGroup的某个childView的dispatchTouchEvent()返回了true,那么ViewGroup会把他设为target,后续的事件都会直接交给他处理,不用一个个调用所有child的dispatchTouchEvent()方法了。
如果view的onTouchEvent()返回false了,那么dispatchTouchEvent()会向上层返回false表明自己没有处理该事件,上层ViewGroup按照处理流程来处理。如果上层的ViewGroup处理流程也返回false,那么会一级一级向上直到activity那里。
view的重绘
<未完待续>







