这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战
以上是进入消息队列的分析,handler调用sendMessage方法的最终将message对象传进messagequeue。
完毕,那么消息是怎么从消息队列出来的呢?
这时我们要回看ActiviryThread的main方法,去寻找点线索。源码在上面已贴出。
发现了倒数第二行的Looper.loop(),简单理解就是消息循环执行循环操作。
这里一定能满足我们的好奇心。那么跟进。loop方法的源码如下:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
复制代码
抓重点看就好。首先是调用myLooper方法获取到Looper对象,这里是没问题的,那就继续
MessageQueue queue = me.mQueue
复制代码
然后从Looper对象中取出关联的消息队列,
接着进入了一个死循环,调用messagequeue的next方法取出message对象。这个next方法我没看懂,所以不贴源码出来分析了,反正next方法的作用就是取出message对象的。有兴趣的同学自己去研究研究吧。
到这里可以总结一下:通过Looper.prepare()来创建Looper(消息循环)对象,然后通过Looper.loop()来执行消息循环,Looper.prepare()和Looper.loop()通常是成对出现的。
好,回来继续
经过一系列的判断后会来到这里,很重点
msg.target.dispatchMessage(msg);
复制代码
上面已经分析,msg.target就是handler,那么这行代码的意义就是调用handler的dispatchMessgage的方法去分发消息,
那么看到dispatchMessage的方法源码,相信谜底就要揭开了
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码
从上述的代码跟踪中,都没有发现给message的callback字段赋值,那么我们就先不搭理,默认callback为空,那么就一定会来到handleMessage方法。
message对象传递到了handleMessage方法。
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
复制代码
handleMessage是一个空方法,需要我们去重写。
至此,海阔天空。完美的从子线程切换到主线程,我不得不说Android的源码设计是多么精彩。
以上就是handler使用sendMessage方法发送消息的源码分析。
为什么我会这么说呢?因为handler还有一种方法可以发送消息,是post方法,理解这个方法。可以解决刚才没搭理的那个message的callback字段的问题。看到post方法源码
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
复制代码
接收一个实现了Runable接口的对象,然后将其传进getPostMessage()方法。跟进getPostMessage()方法看看
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
复制代码
其实就是将Runable包装成message的callback嘛。
所以,如果我们使用post方法发送消息,callback字段是不为空的,那么就会执行handleCallback()方法,而不是执行handleMessage方法了。
handleCallback方法源码如下:
private static void handleCallback(Message message) {
message.callback.run();
}
复制代码
直接是调用run方法,表明我们直接在run方法里进行UI操作就行了。
我们发现不管是使用post方法还是sendMessage方法来发送消息,最终都会调用sendMessageDelayed方法。handler将消息追加到消息队列中的过程都是一样的,然后Looper不断的从MessageQueue中取出消息,并由handler去分发消息,处理消息,这样就构成了完善的Android消息机制体系。
最后在总结一下,主线程创建 Handler,关联 Looper 和 MessageQueue。在子线程中执行业务逻辑获取到数据后,此时我们需要切换到主线程,那就创建一个Message对象,包装一些数据,然后调用 Handler 的 sendMessage 方法发送消息到消息队列 MessageQueue。神奇的是,发送的时候,Message的 target 字段赋值上了 Handler,也就是说 Handler 对象跟着 Messgae 对象进入了消息队列 MessageQueue。在程序启动的时候, Looper 就调用了 loop 方法,不断的从消息队列 MessageQueue 中取出消息,一旦 Messgae 进入消息队列,Looper 从消息队列取出消息后,Messgae 中包装的 Handler,会调用 dispatchMessage 去分发消息,将消息传进 handleMessage 方法,这时就切换到了主线程。这就是消息机制。