上一篇博客Handler 之 初识及简单应用中介绍了 Handler 的作用,以及 Handler 的基本用法,同时也详细介绍了为什么子线程不能更新 UI 的原因,但是因为篇幅原因,所以对 Handler 的内部机制并没有展开叙述。这篇文章就从 Handler 开始解析与之相关的源码,从而更好的理解 Handler 以及 Looper MessageQueue。
Handler 机制
写完上一篇文章,下面我该再读一遍 Handler 的源码了,其实讲 Handler 内部机制的博客已经很多了,但是自己还是要在看一遍,源码是最好的资料。
在具体看源码之前,有必要先理解一下 Handler、Looper、MessageQueue 以及 Message 他们的关系。
关系
Looper: 是一个消息轮训器,他有一个叫 loop() 的方法,用于启动一个死循环,不停的去轮询消息池。
MessageQueue: 就是上面说到的消息池
Handler: 用于发送消息,和处理消息
Message: 一个消息对象
现在要问了,他们是怎么关联起来的?
一切都要从 Handler 的构造方法开始。如下所示
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;
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 本身定义了一个 MessageQueue 对象 mQueue,和一个 Looper 的对象 mLooper。
不过,对 Handler 的这两个成员变量的初始化都是通过 Looper 来赋值的。
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
现在,我们新建的 Handler 就和 Looper、MessageQueue 关联了起来,而且他们是一对一的关系,一个 Handler 对应一个 Looper,同时对应一个 MessageQueue 对象。这里给 MessageQueue 的赋值比较特殊,
mQueue = mLooper.mQueue;
这里直接使用 looper 的 mQueue 对象,将 looper 的 mQueue 赋值给了 Handler 自己,现在 Looper 和 Handler 持有着同一个 MessageQueue 。
这里可以看到 Looper 的重要性,现在 Handler 中的 Looper 实例和 MessageQueue 实例都是通过 Looper 来完成设置的,那么下面我们具体看看 Looper 是怎么实例化的,以及他的 mQueue 是怎么来的。
Looper
从上面 Handler 的构造方法中可以看到,Handler 的 mLooper 是这样被赋值的。
mLooper = Looper.myLooper();
接着看 myLooper() 的实现。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这里出现了一个平时不怎么看到的 ThreadLocal 类,关于这个类,推荐去阅读任玉刚的一篇文章 - Android的消息机制之ThreadLocal的工作原理,讲的很不错。另外自己也写了一篇文章,用于讲解 ThreadLocal 的用法,以及他在 Handler 和 Looper 中的巧妙意义。
任玉刚 - Android的消息机制之ThreadLocal的工作原理
这里他是通过 ThreadLocal 的 get 方法获得,很奇怪,之前我们没有在任何地方对 sThreadLocal 执行过 set 操作。 这里却直接执行 get 操作,返回的结果必然为空啊!
但是如果现在为空,我们在 new Handler() 时,程序就已经挂掉了啊,因为在 Handler 的构造方法中,如果执行 Looper.myLooper() 的返回结果为空。
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
但是,我们的程序没有挂掉,这意味着,我们在执行 myLooper() 方法时,他返回的结果不为空。
为什么呢?那我们在 Looper 中看看,哪里有对应的 set 方法,如下所示,我们找到了一个全局静态方法 prepare
public static void prepare() {
prepare(true);
}
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));
}
我们看到,在最后一行这里,执行了对应的 set 操作,这里把一个 new 出来的 Looper 直接 set 到 sThreadLocal 中。
但是我们不知道,到底什么时候,是谁调用了 prepare() 方法,从而给 sThreadLocal 设置了一个 Looper 对象。
后来在网上经过搜索,找到了答案,我们的 Android 应用在启动时,会执行到 ActivityThread 类的 main 方法,就和我们以前写的 java 控制台程序一样,其实 ActivityThread 的 main 方法就是一个应用启动的入口。
在这个入口里,会做很多初始化的操作。其中就有 Looper 相关的设置,代码如下
public static void main(String[] args) {
//............. 无关代码...............
Looper.prepareMainLooper();
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
在这里,我们很清楚的看到,程序启动时,首先执行 Looper.prepareMainLooper() 方法,接着执行了 loop() 方法。
先看看 prepareMainLooper 方法。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
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));
}
这里,首先调用了 prepare() 方法,执行完成后,sThreadLocal 成功绑定了一个 new Looper() 对象,然后执行
sMainLooper = myLooper();
可以看看 sMainLooper 的定义,以及 myLooper() 方法;
private static Looper sMainLooper; // guarded by Looper.class
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
现在的 sMainLooper 就有值了,也就是说,只要我们的 App 启动,sMainLooper 中就已经设置了一个 Looper 对象。以后调用 sMainLooper 的 get 方法将返回在程序启动时设置的 Looper,不会为空的。
下面在看 main 方法中的 调用的 Looper.loop() 方法。 已经把一些无关代码删了,否则太长了,
public static void loop() {
//获得一个 Looper 对象
final Looper me = myLooper();
// 拿到 looper 对应的 mQueue 对象
final MessageQueue queue = me.mQueue;
//死循环监听(如果没有消息变化,他不会工作的) 不断轮训 queue 中的 Message
for (;;) {
// 通过 queue 的 next 方法拿到一个 Message
Message msg = queue.next(); // might block
//空判断
if (msg == null)return;
//消息分发
msg.target.dispatchMessage(msg);
//回收操作
msg.recycleUnchecked();
}
}
现在,想一个简单的过程,我们创建了一个 App,什么也不做,就是一个 HelloWorld 的 Android 应用, 此时,你启动程序,即使什么也不干,按照上面的代码,你应该知道的是,现在的程序中已经有一个 Looper 存在了。 并且还启动了消息轮询。 Looper.loop();
但是,目前来看,他们好像没什么用,只是存在而已。
此时你的项目如果使用了 Handler,你在主线程 new 这个 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;
}
此时的 myLooper() 返回的 Looper 就是应用启动时的那个 Looper 对象,我们从 Looper 的构造方法得知,在 new Looper 时,会新建一个对应 的消息池对象 MessageQueue
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
那么在 Handler 的构造方法中,那个 mQueue 其实也是在应用启动时就已经创建好了。
现在再来回顾一下 Handler 的构造方法,在构造方法中,他为自己的 mQuery 和 mLooper 分别赋值,而这两个值其实在应用启动时,就已经初始化好了。
并且,现在已经启动了一个消息轮训,在监听 mQuery 中是不是有新的 Message !
现在这个轮训器已经好了,我们看发送消息的过程。
SendMessage
我们使用 Handler 发送消息
mHandler.sendMessage(msg);
Handler 有好多相关的发送消息的方法。但是追踪源码,发现他们最终都来到了 Handler 的这个方法 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);
}
接着看 enqueueMessage() 方法体
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
// 使用默认的 handler 构造方法时,mAsynchronous 为 false。
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里有一句至关重要的代码
msg.target = this;
我们看看 msg 的 target 是怎么声明的
Handler target;
意思就是每个 Message 都有一个类型为 Handler 的 target 对象,这里在 handler 发送消息的时候,最终执行到 上面的方法 enqueueMessage() 时,会自动把当前执行 sendMessage() 的 handler对象,赋值给 Message 的 target。
也就是说,Handler 发送了 Message,并且这个 Message 的 target 就是这个 Handler。
想想为什么要这么做?
这里再说一下,Handler 的作用,
- 发送消息
- 处理消息
先不看代码,我们可以想想,handler 发送了 message ,最终这个 message 会被发送到 MessageQueue 这个消息队列。 那么最终,谁会去处理这个消息。
在这里消息发送和处理遵循『谁发送,谁处理』的原则。
现在问题来了,就按照上面说的,谁发送,谁处理,那现在应该是 handler 自己处理了,但是他在哪里处理呢?到现在我们也没看到啊。
慢慢来,接下来继续看消息的传递。
现在,我们只要发送了消息,那么消息池 mQuery 就会增加一个消息,Looper 就会开始工作,之前已经说了,在应用启动的时候, 已经启动了 Looper 的 loop() 方法,这个方法会不断的去轮训 mQuery 消息池,只要有消息,它就会取出消息,并处理,那他是怎么处理的呢?看一下 loop() 的代码再说。
public static void loop() {
//获得一个 Looper 对象
final Looper me = myLooper();
// 拿到 looper 对应的 mQueue 对象
final MessageQueue queue = me.mQueue;
//死循环监听(如果没有消息变化,他不会工作的) 不断轮训 queue 中的 Message
for (;;) {
// 通过 queue 的 next 方法拿到一个 Message
Message msg = queue.next(); // might block
//空判断
if (msg == null)return;
//消息分发
msg.target.dispatchMessage(msg);
//回收操作
msg.recycleUnchecked();
}
}
看 for()循环,他在拿到消息后,发现 msg 不为空,接着就会执行下面这句非常重要的代码
msg.target.dispatchMessage(msg);
这里执行了 msg.target 的方法 dispatchMessage,上面已经在 sendMessage 时看到了,我们在发送消息时,会把 msg 的 target 设置为 handler 本身,也就是说,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);
}
}
我们看到,我们没有给 msg 设置 callback 也没有给 handler 的 mCallback 设置过值,所以此时,会执行 handleMessage() 方法;
public void handleMessage(Message msg) {
}
发现这是一个空方法,所以自己的新建 Handler 时,只要复写了这个方法,我们就可以接受到从子线程中发送过来的消息了 。
在看一遍自己定义 Handler 时,如何定义的,如何复写 handlerMessage
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
Bitmap bitmap = (Bitmap) msg.obj;
imageView.setImageBitmap(bitmap);
break;
case -1:
Toast.makeText(MainActivity.this, "msg "+msg.obj.toString(), Toast.LENGTH_SHORT).show();
break;
}
}
};
在这里,我们处理了自己发送的消息,到此 Handler 的内部机制大体就分析完了。
但是从上面的 dispatchMessage 方法我们也能看出,Handler 在处理消息时的顺序是什么?
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
他首先判断 Message 对象的 callback 对象是不是为空,如果不为空,就直接调用 handleCallback 方法,并把 msg 对象传递过去,这样消息就被处理了。
如果在发送消息时,我们没有给 Message 设置 callback 对象,那么程序会执行到 else 语句块,此时首先判断 Handler 的 mCallBack 对象是不是空的,如果不为空,直接调用 mCallback 的 handleMessage 方法进行消息处理。
最终,只有当 Handler 的 mCallback 对象为空,才会执行自己的 handleMessage 方法。
这是整个处理消息的流程。
现在就会想了,在处理消息时,什么时候才能执行到第一种情况呢,也就是通过 Message 的 callback 对象处理。
其实简单,查看源码发现
/*package*/ Runnable callback;
callback 是一个 Runnable 接口,那我们这怎么才能设置 Message 的 callback 的参数呢?最后观察发现,Handler 发送消息时,除了使用 sendMessage 方法,还可以使用一个叫 post 的方法,而他的形参正好就是 Runnable,我们赶紧拔出他的源码看看。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
接着看 getPostMessage() 这个方法。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
代码看到这里,已经很清楚了,getPostMessage() 返回了一个 Message 对象,这个对象中设置了刚才传递过来的 runnable 对象。
到这里,你应该明白了,在处理消息时,除了 Handler 自身的 handlerMessage() 方法设置处理,还可以直接在发消息时指定一个 runnable 对象用于处理消息。
另外上面通过 dispatchMessage() 的代码已经看出来,处理消息有三种情形,第一种直接使用 Message 的 running 对象处理,如果不行使用第二种 用 Handler 的 mCallback 对象处理,最后才考虑使用 handlerMessage 处理,关于第二种情形,这里就不分析了,自己试着看代码应该能找到。Good luck ~