面试问题总结之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_CONFIGURATIONandroid: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的重绘

<未完待续>