Handler的工作原理
引入一个BUG
在介绍工作原理之前,先引入一个问题(BUG),为什么在子线程中执行 new Handler() 会抛出异常?
new Thread(new Runnable() {
@Override
public void run() {
Handler handler = new Handler();
}
}, "Thread#1").start();
崩溃如下
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:203)
出现BUG,最好的老师就是看源代码,因此跟踪源代码可以看到,在构造函数中
public Handler(Callback callback, boolean async) {
…
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
…
}
所以崩溃的原因是 如果当前线程没有Looper话,就会抛出如上的崩溃。
因此,我们的解决方法是
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
}, "Thread#1").start();
这样创建的工作线程就具有了消息处理机制。FIX BUG,上线吧。
工作原理
虽然解决了BUG,但是我们还需要更一步的理解Handler
的工作原理,上面的构造函数只是源代码的冰山一角。
Handler
的主要工作包含消息的发送和接收处理两个过程。
消息的发送
一般我们会调用handler.sendMessage(msg)
,源代码是我们的好老师,进入源代码查看具体调用实现。
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
跟踪源代码的调用,Handler
发送的消息最后是向消息队列MessageQueue
插入了一条消息。
(跳跃一下)MessageQueue
的next方法就会返回这条消息给Looper
,Looper
收到消息后就开始处理。最终消息由Looper
交给Handler
处理,即Looper
源代码中的msg.target.dispatchMessage(msg)
,这里的msg.target
就是对应的的Handler
。
这也解释了一个问题(问题少年),如果有两个不同的Handler发消息,那么消息怎么知道是哪个Handler发送和处理的?就是通过上面的msg.target
。
这时就进入下个环节,Handler
处理消息的阶段。
消息的处理
继续跟着源代码老师学习,消息的处理就需要看Handler
的dispatchMessage
方法实现。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
从上面可以看出,Handler处理消息的过程如下
1、检查Message
的callback
是否为null,不为null,则直接调用Handler
的handleCallback
处理。
这种场景对应的是用Handler.post(Runnable runnable)
发送消息。处理消息的handleCallback
实现如下:
private static void handleCallback(Message message) {
message.callback.run();
}
message.callback
就是Runnable
实例。
2、检查mCallback
是否为null,不为null则调用mCallback
的handleMessage
处理。
这种场景对应的是初始化Handler时传入CallBack对象参数,即
public Handler(Callback callback) {
this(callback, false);
}
这是另外一种使用Handler
的方式,处理消息交给Callback
的实现方法handleMessage
。
3、当上面两种场景不存在,或者存在场景2,但返回值为false(即需要进一步处理)时,就调用Handler
的handleMessage
方法处理。
这种场景对应的是
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
...
}
};
handler.sendMessage(msg);
总结归纳也是学习的好老师,把Hander
处理消息的过程总结成一个流程图,如下:
![](https://img.haomeiwen.com/i2707477/54e2f5ed9c8bdf93.png)
扩展问题
好奇是学习的另一位良师,那么问题来了。
问题1:接着开始的BUG,为什么我们在Activity初始化Handler时,没有写Looper.prepare
,但不会崩溃?
这是因为Activity所在的UI线程(主线程)在初始化时,创建了一个消息队列和消息循环,具体可以看源代码的ActivityThread
类的main
方法。
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
Looper.loop();
...
}
问题2:如何直接获取UI线程(主线程)的Handler?
解决方式如下
Hander mHandler = new Handler(Looper.getMainLooper());
传入主线程的Looper, 初始化Handler一步到位。
参考
任玉刚 《Android开发艺术探索》
Handler的使用 by自己