引入一个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处理消息的过程总结成一个流程图,如下:
Handler消息处理流程图.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自己








网友评论