最近在看《深入理解Android内核设计思想》,看到有关Handler消息机制这部分,以前一直对这块似懂非懂,其实说白了就是还不懂,现在看过这篇后可以说是受益颇多,作者从源码的角度深层次的解析加上形象生动的语言描述,可谓良书一本,下面就针对自己对整个消息机制的理解做个总结。
我们都知道,Android在子线程中直接更新UI操作时,会报出异常
android.view.ViewRoot$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.
意思就是说,只有原始的线程(主线程/ui线程)才能修改view对象。在子线程中修改view的显示状态,会报上面的异常
这是为什么呢?
因为在Android系统中UI主线程要负责执行UI的渲染、View的绘制,这些操作都需要非常高的时效性,以保证界面不会卡顿,甚至无响应(ANR),而子线程有可能需要执行较长时间的耗时操作,比如连接网络获取数据、读取数据库获取数据等,如果把UI操作都放在子线程,则UI操作就必须等待耗时操作执行完才能绘制出来,这样便很容易引起界面无响应。
所以Android便引入了消息机制,来实现子线程和主线程之间的通信传递数据。
简单来总结一下就是:子线程获取到数据之后,不直接进行UI更新,而是把数据’装’到消息中发送到主线程,主线程中有一个循环轮询会立即收到子线程发送过来的信息,然后拿到消息数据后在主线程更新UI。
做法:通常是在主线程new一个handler,然后子线程通过handler来发送消息。最终是在handler的handleMessage方法中处理子线程发送过来的数据消息,直接进行UI更新。
那么消息机制到底是怎样运作的呢?下面开始一步步探讨
先来简单认识一下它们之间的关系和作用:
从字面上理解,Handler的意思是:“处理者”,那么Handler处理的是什么呢?下面我们开始一步步探讨
在Android代码中,当我们在创建Handler实例,则会报如下错误:
意思就是说:在没有调用Looper.prepare()之前不能在子线程创建Handler(那么为什么不能这样?看完解析第二步马上会有答案)
先来解释一下,为什么在主线程中我们就已经可以直接创建Handler?
因为在Activity的启动代码中,已经在当前UI线程(主线程)调用了Looper.prepareMainLooper()和Looper.loop()方法。我们可以在源码的ActivityThread类中看到,如图:
从上图可以看出,在主线程中首先调用的是Looper.prepareMainLooper(),然后创建了一个ActivityThread实例,最后通过Looper类使主线程进入消息循环中。
3.Looper.loop();
示例:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
public class Looper {
......
private static final ThreadLocal sThreadLocal = new ThreadLocal();
final MessageQueue mQueue;
......
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
......
}
在调用 Looper.prepare() 方法时,创建一个 Looper 对象,这个对象是被 set 绑定到一个ThreadLocal(线程局部变量)中
sThreadLocal.set(new Looper());
ThreadLocal的作用就是保证每一个调用了prepare()函数的线程里面都有一个唯一的Looper对象。
意思就是说:如果在一个子线程中创建一个 Handler,那么它首先调用 Looper.perpare()方法时,创建的 Looper 对象是新的,与主线程不同。
new Looper();
在创建一个 Looper 对象时,同时 new 了一个MessageQueue消息队列,后续消息就是存放在这个队列中去的,这会就可以很明白知晓最开始的那张关系图了,即 Looper 中包含了一个 MessageQueue【这句话非常重要】。
public Handler mHandler;
...
mHandler = new Handler() {
public void handleMessage(Message msg) {
...
}
};
我们在 new Handler()时,Handler 的构造函数有如下几种:
public Handler();
public Handler(Callback callback);
public Handler(Looper looper);
public Handler(Looper looper, Callback callback);
之所以有这么多构造函数,是因为 Handler 有如下内部变量需要初始化。
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()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从上面代码可以看出,创建一个 Handler 时,需要获取 Looper 轮询器和 MessageQueue 消息队列,它们都作为 Handler 的成员变量,这样 Handler 和 Looper,MessageQueue 就联系起来了。
现在终于可以解释为什么在没有调用Looper.prepare()之前不能在子线程创建Handler:
因为在new Handler 的时候,首先先要有 Looper 轮询器对象(在Handler底层中是通过Looper.myLooper()获取),然后在 new Looper 轮询器的同时new MessageQueue,然后Handler在底层中再通过Looper对象的成员属性mQueue获取到MessageQueue。这样在 Handler 的构造函数中才能获得 looper和 messageQueue
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
...
msg.target.dispatchMessage(msg);
...
}
}
上面代码比较多,我们就看一句关键的代码 msg.target.dispatchMessage(msg);
从上面代码也可以看出,轮询器在for (;;){}死循环代码块中不断的执行, 通过 queue.next();从 MessageQueue 中取出一个 Message,当msg不为空时,执行msg.target.dispatchMessage(msg);(实际上最终是调用到了Handler的dispatchMessage方法去拦截消息)
在Message 中的源码中,target 就是当前线程的 Handler对象,msg的成员变量target是在发送消息的时候设置好的,一般就通过哪个Handler来发送消息,就通过哪个Handler来处理消息。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
继续调用 handleMessage(msg);
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
意思就是说:每次我们 new Handler()的时候必须实现handleMessage(msg)方法才能接收到message。
我们一般在子线程中通过handler.sendMeaaage(msg)来发送消息,由于在子线程中禁止更新 UI,所以我们可以通过子线程中处理获得的数据,通过 handler 发送出去,这样数据便传递到了主线程,最终回到 handleMessage(msg)这个方法中对消息进行处理。
我们来看 Handler 的源码:
handler.sendMessage(msg)最终调用的是sendMessageAtTime
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);
}
从上面的代码可以看出:handler最终是把 msg 压到了 MessageQueue 中。
这个时候在MessageQueue消息队列中,就有了 Message 消息,由于 Looper 是死循环的执行 loop()方法不断的轮询从MessageQueue中取出 message,最终又把 message 传递到了在主线程的 handler 让他去执行handleMessage(msg)来处理一开始从子线程发过来的 message。
结论:Handler 还负责将某个消息压入 MessageQueue 中(发消息)