前言
最近通过微信公众号推文以及博客文章学习了Android的消息机制的原理,然后抽空写下了这篇文章,对自己学到知识进行梳理,也方便以后自己查阅。
因为Android的UI是线程不安全的,而我们处理完成异步任务的时候,常常都需要更新我们的UI界面,这样,就产生了一个矛盾:也就是我的子线程需要更新UI,而这个操作又必须在主线程中完成。所以,就有了我们接下来要分析的消息机制。
消息机制的大概流程:
启动系统后,会在主线程创建一个Looper对象,然后通过这个Looper对象开启一个死循环,循环会不断地从消息队列中(MessageQueue)中取出待处理的消息(Message),并回调Handler中的方法处理消息。消息队列中的消息是从哪里来的呢?没错,就是通过Handler发送过去的。流程也可用下面的图表示出来:
消息机制源码分析:
我们在使用到消息机制进行更新UI的时候的操作步骤是怎么样的呢?
- 在主线程中创建一个Handler对象,传入一个Handler.Callback接口的匿名类对象,并重写该对象的handlerMessage()方法。
- 在子线程中创建一个Message对象,把要发送的消息放在Message对象中,然后调用Handler的sendMessage()方法把Message插入到MessageQueue中
- 最后主线程中重写的handlerMessage()方法会被调用,更新UI的操作就完成啦
我们就跟着上面的步骤去看一下消息机制里面都做了些什么吧。没错,我们就要去看看源码了,其实看源码不难,跟着思路来走就不会乱了。首先进入Handler的构造方法:
//我们传入Handler.Callback对象调用的构造方法
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//获取Looper对象
//如果Looper对象为空,就直接抛出异常,提示用户没有使用Looper.prepare()创建Looper对象
//我们还没创建过Looper对象,会不会报错呢?不急,我们接着往下看 ,肯定与Looper.prepare()有关
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//获取MessageQueue消息队列
//其实MessageQueue里面的数据结构是一个单链表
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
好啦,这样,我们的一个Handler对象就创建好了(Looper.prepare()这个方法我们后面会讲到,先顺着思路走下去),并且我们可以看到,Handler中持有Looper和MessageQueue的引用。接下来我们就要用到这个Handler对象往消息队列中插入消息了,让我们看看Handler.sendMessage(),看它是如何往消息队列中插入消息的
//我们使用时调用的方法
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) {
//target其实就是Handler对象,通过msg.target=this实现Message与Handler的绑定
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//最后,调用了MessageQueue的enqueueMessage方法
return queue.enqueueMessage(msg, uptimeMillis);
}
//下面的源码中省略了一些无关、影响阅读的源码,想看完整源码的可以自行去AS中查看
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
Message p = mMessages;
if (p == null ) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
} else {
Message prev;
//前面说过,MessageQueue数据结构是单链表,下面就是遍历单链表,找到链表尾部
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;//把消息插入到尾部
}
}
return true;
}
这时候,我们就已经成功把消息(Message)插入到MessageQueue中了,并且我们知道了Handler把自己封装进了Message当中。走完这两步,我们好像走进了一个死胡同,走不下去了?先来看看下面几个问题:
- Looper对象是在哪里被创建的?
- Handler中的handlerMessage()方法是怎么被回调的?
我们在上面的步骤中,没有创建过Looper对象,而Looper对象却已经存在了,那唯一的一种可能就是,当我们启动系统的时候,系统已经帮我们创建好Looper对象了。我们在ActivityThread类中找到main()方法,ActivityThread类是一个隐藏类,我们可以在SDK文件夹中查找出来:
public static void main(String[] args) {
......
Looper.prepareMainLooper(); //创建一个Looper对象
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); //开启循环
throw new RuntimeException("Main thread loop unexpectedly exited");
}
果然,我们能在main()发现Looper。main()方法中通过Looper.prepareMainLooper()创建一个Looper对象,prepareMainLooper()方法实际上就是调用Looper.prepare(),没错,就是我们之前发现的方法,接下来让我们看看这个方法做了什么吧
public static void prepareMainLooper() {
prepare(false);
......
}
......
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()中就是new了一个Looper,并把Looper set进ThreadLocal中(ThreadLocal是什么来的?我暂时还没有去了解,有时间会补充),所以Looper在系统启动时就创建好了,我们创建Handler时才不会报错
Looper创建完成之后,然后Looper.loop()就会开启一个循环,开启循环做什么呢?就是之前说过的,从MessageQueue中不断的取出消息来
//剔除了一些影响阅读的代码后,可以看到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;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
} finally {
......
}
}
}
很明显可以看出loop方法一直遍历MessageQueue,阻塞线程,直到获取到一个Message,然后调用Message的一个成员变量target的dispatchMessage方法。之前说过了,target其实就是Handler,dispatchMessage方法最终就调用我们重写的Handler的handlerMessage方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
以上就是我们在主线程中消息机制的原理,让我们整理一下上面的知识:
- Handler:在消息机制中,是作为消息的发送方和处理方。消息在一个线程中通过Handler发送到MessageQueue中。
- Looper:在消息机制中,是作为消息队列的管家。不停的从消息队列中获取消息,获取到消息后,根据Message中绑定的Handler对象调用Handler中的dispatchMessage方法,进而调用到开发者重写的handlerMessage方法进行消息的处理
- MessageQueue和Message:MessageQueue就是存放Message的地方,实质上是一个单链表的结构,有新的Message来的时候,就把它放在表尾,当Looper来取消息时就把表头的Message给它。Looper拿到消息后,可以从Message中拿到消息的发送方。
知道消息机制的原理后,我们就可以轻松的在任何线程下使用handler了:
new Thread() {
@Override
public void run() {
Looper.prepare();
Looper.loop();
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
})
}
}.start();
- 首先用Looper.prepare()创建一个Looper并初始化Looper持有的MessageQueue
- 然后用Looper.loop()方法开启循环,从MessageQueue中取消息,并调用handler的dispatchMessage方法处理消息,如果消息队列里面没有消息,循环就会阻塞进入休眠状态,等有消息时就会被唤醒
- 最后再new一个Handler,Handler构造方法会获取到Looper和Looper的MessageQueue对象,然后通过sendMessage方法往消息队列中插入消息。
最后,我们对消息机制有一些了解了,先写这么多吧,如有错误欢迎指出。