Android 的异步消息处理机制
线程是CPU独立运行和独立调度的基本单位,在 Android 中主线程,又叫UI线程,这是一个特殊的线程,任何耗时的操作最好不要在UI线程中执行,因为这有可能造成 ANR。so,耗时任务只能在其它线程搞事,完成后再通知 UI 线程更新界面。这就需要理解Android的异步消息处理。
关于 Android 的异步消息处理机制,一直以来,知其然而不知其所以然。带着好奇,搜了几篇博客,然后跟着鸿洋大神的这篇博客的思路,浏览了一下 Handler、Looper、MessageQueue、Message、ActivityThread 这几个类的源码,忽然间,豁然开朗。具体的原理,大神文中已经分析得比较清楚,这里对阅读过程中想到的几个问题做做笔记。
一、只有 UI 线程才有 Looper 对象吗?
答案是否定的,普通的 Thread 也可以有 Looper 对象。普通的线程也可以在创建的时候创建 Looper 对象,UI 线程之所以看起来天生自带 Looper 对象,其实是在其创建的时候创建了一个Looper对象:
// ActivityThread#main
public static void main(String[] args) {
// ...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
// ...
Looper.loop();
// ...
}
其实跟踪源码,在 Looper#prepareMainLooper()中并没有创建 Looper,查看其注释,发现这么一句:The main looper for your application is created by the Android environment。原来是在其它地方创建的,这里只是把这个Looper标记为Application的main Looper而已。
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
二、如何创建一个 Looper Thread?
创建一个线程,在run中调用先后调用 Looper.prepare(),Looper.loop(),在prepare()中创建一个 Looper 对象,存放在sThreadLocal中,要成为一个 Looper Thread 还需要调用 loop(),在loop()中线程进入一个循环,在循环中处理消息,然后可以通过Handler向线程发送消息。HandlerThread 是一个 Looper Thread,它的 run()方法如下:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
Looper#prepare()代码如下,首先检查 sThreadLocal 是否为null,如果不是,那么抛出一个RuntimeException,如果是,则创建一个Looper对象,并存在sThreadLocal 对象中。所以,一个线程,只能调用一次Looper#prepare()。
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));
}
三、Thread和 Looper Thread有什么区别呢?
一段代码,如果不是使用了while(true)、for(;;) 等死循环的逻辑,按逻辑顺序跑完了就完了。没有Looper对象的 Thread,当 run 方法跑完的时候自动退出了, Looper Thread,在 Looper.loop() 时进入了一个死循环,该循环不断地从消息队列中读取和处理消息。UI 线程就是一个 Looper 线程。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
// ...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ...
msg.recycleUnchecked();
}
}
注意这段代码,第六行 if 中判断 msg 为null则跳出循环,看起来消息处理完就退出这个线程了。实际上在它的上一句中,queue.next(),这里如果没有消息则线程阻塞等待,然后等有新的消息进入队列才唤醒线程。
四、Handler发送的消息是如何回到 handleMessage() 中处理的?
首先需要知道:在哪个线程创建的Handler,应该说,Handler 对象持有的是哪个线程的 Looper 对象,消息就会发送的哪个线程处理。
4. 1 Handler 发送消息
Handler 发送消息的方法有:post()、postAtTime()、postDelayed()、sendMessage()、sendMessageDelayed()、sendMessageAtTime()、sendEmptyMessage()、sendEmptyMessageDelayed()、sendEmptyMessageAtTime()等等。实际上最后调用的是Handler#enqueueMessage(),将 Message 插入到 Looper 的消息队列中:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler#enqueueMessage() 中,调用的是MessageQueue的enqueueMessage():
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
在最后倒数第5行中,如果需要唤醒则调用了一个native方法,然后回到了loop的消息循环中的queue.next()
4.2 Looper 轮询 MessageQueue
在 Looper#loop() 中,线程进入了一个死循环,不断地从 MessageQueue 中读取 Message 然后交给 Message 的 target处理:
public static void loop() {
final Looper me = myLooper();
// ...
for (;;) {
Message msg = queue.next(); // might block
// ...
msg.target.dispatchMessage(msg);
// ...
}
}
Message#target 指向的就是发送这个Message 的Handler 对象,所以,消息从哪里来,最终又到哪里去,兜了一圈,然后回到了Handler#dispatchMessage() :
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
4.3 Handler 处理消息
在 Handler#dispatchMessage() 中,最后调用了我们重写的 handleMessage()
class MyActivity extends Activity{
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
// 更新 UI
}
}
}
总结
Thread、Handler、Looper、Message、MessageQueue之间的关系:
1. Looper Thread中有且只有一个Looper对象
2. Looper对象有一个消息队列 MessageQueue 对象
3. Handler对象持有Thread的Looper对象,和 Looper 对象的 MessageQueue 对象
线程(如UI线程)在Looper#loop() 中进入消息循环,不断地从消息队列中取出消息进行处理,如果没有消息则阻塞,等待消息进入队列,然后唤醒。handler 对象 向 Looper Thread 发送 Message,最终调用的是Handler 的 enqueueMessage(),enqueueMessage()中将Message插入消息队列,Looper在循环处理消息的时候,将消息从消息队列中取出,通过Message的target属性,将消息交还给发送消息的 Handler 对象处理,最终,调用我们重写的 handleMessage() 方法将消息交给线程处理。