本文是对常见面试问题的分析,关于Handler的运行机制详细分析见:Android Handler的源码分析
常见问题包括:
1、Handler是怎么实现切换线程的?
2、handler.sendMessage()与handler.post()的区别?
3、MessageQueue是怎么增删消息的?
4、一个线程可以有几个Handler?几个Looper?几个MessageQueue?
5、A Handler发送的消息为什么不会跑到B Handler的handleMessage()方法中?
6、简述ThreadLoacal的原理?
7、Handler引起的内存泄漏?
8、在UI中创建的Handler,通过post方式发送的消息在run方法中可以进行UI更新吗?
1、Handler是怎么实现切换线程的?
2、handler.sendMessage()与handler.post()的区别?
查看Handler可以发现,最终发送至MessageQueue的方法都是Handler中的enqueueMessage(),但是对于Message的处理,在分发时,会将消息的结果返回至其callback 中(如果不为空)。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
3、MessageQueue是怎么增删消息的?
增加:通过enqueueMessage方法,通过对消息添加时间和之前消息的判断,将新添加的消息放置在栈顶
msg.next = p; // invariant: p == prev.next
prev.next = msg;
删除:通过next方法取出消息,在loop()中进行消息的分发处理,在next返回消息前对消息属性进行修改
// MessageQueue中的next()方法
msg.next = null;
msg.markInUse();
// Message中的markInUse()方法,表明消息已经使用
void markInUse() {
flags |= FLAG_IN_USE;
}
4、一个线程可以有几个Handler?几个Looper?几个MessageQueue?
多个Handler?
**多个:**上述描述中,知道之所以消息可以准确的分发处理,是由于通过Message的target方法在添加和分发时标注Handler,实现准确的分发,因此同一个线程可以创建多个Handler对象,例如常见的UI线程中:
private Handler mHandler= new Handler(Looper.myLooper()){...}
几个Looper?
一个:在2.2中主线程发送至子线程中,需要先调用Looper.prepare()方法,否则会抛出异常,那如果一个线程中包含多个Looper会怎样?
查看源码可以发现,如果包含多个Looper对象会抛出异常。
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
几个MessageQueue?
一个:在调用Looper.prepare()时,会判断该线程的Looper是否为空,只有为空的情况才会调用Looper的构造方法,创建MessageQueue,因此只有一个。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
5、A Handler发送的消息为什么不会跑到B Handler的handleMessage()方法中?
参考问题1和4可知,一个线程中包含多个Handelr对象,而在消息的添加分发时,通过Message的target(Handler)标注。
6、简述ThreadLoacal的原理?
在3.1中新建Handler时,已对ThreadLoacal简述,通过set和get方法,其内部维护这一个Map集合,其中key是当前的线程,value为需要存储的值。
7、Handler引起的内存泄漏?
可参考Android中使用Handler为何造成内存泄漏?中相关的介绍,主要是由于在消息处理时,当前Activty已经关闭,但是仍有消息处理,导致当前Activty无法正常消耗出现内存泄漏的情况,解决方案:
详细分析参考:Android Handler之原理解析
8、在UI中创建的Handler,通过post方式发送的消息在run方法中可以进行UI更新吗?
可以:通过查看源码可以发现,其最终调用的还是sendMessage相关的方法,但是如果传入的Runnable形式,其内部会对其进行了封住成Message类。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
在消息处理结束后,其回调至Runnable 的run方法中,需要注意的是这里的仍然是在UI线程中,因为我们创建的Handler是在UI线程中,且Handler将Runnable内部封住成Message的形式,在消息分发时首先检测callback是否为空,如下所示:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
...
}
}
如果不为空则调用handleCallback方法,但是该方法实际上是调用message.callback.run(),而message.callbac是我们在添加消息时赋值的Runable,所以最终调用的是Runable中的run()方法,因此还是回到UI线程中,可以更新UI界面。
private static void handleCallback(Message message) {
message.callback.run();
}