从源码解析Android异步消息处理机制(一)

从源码解析Android异步消息处理机制(一)


前言:本篇纯属个人学习笔记,故摘录《Android开发艺术探索》大量语句,特此声明


  • 目的:深入理解Looper、Handler、MessageQueue三者之间的关系。

    1. 概述


Android的消息机制主要指Handler的运行机制,Handler的运行又和MessageQueue、Looper这两者紧密相关。简单来说,异步消息处理线程启动后,Looper会以无限循环的形式去MessageQueue查找是否有新的消息,如果有的话就回调相应的消息处理函数,否则就一直等待着(阻塞)。

2.源码解析

MessageQueue

MessageQueue(Android.os.MessageQueue类),消息队列。它的内部存储了一组消息,以队列的形式对外提供插入和读取的工作(读取操作本身会伴随着删除操作)。虽然叫消息队列,但是它的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表。插入和读取对应的方法分别是enqueueMessage()和next()。
我们主要看next()方法(而enqueueMessage()方法有兴趣的道友可自行查看源码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}

// We can assume mPtr != 0 because the loop is obviously still running.
// The looper will not call this method after the loop quits.
nativePollOnce(mPtr, nextPollTimeoutMillis);

synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
}

可以发现next()方法是一个无限循环的方法,如果MessageQueue没有消息,那么next()会一直阻塞在那里,当有新消息时,next方法会返回这条消息并将其从单链表中移除。

Looper

Looper(Android.os.Looper类),消息循环。由于MessageQueue只是一个消息的存储单元,它不能去处理消息,而Looper就填补了这个功能,Looper会以无限循环的形式去MessageQueue查找是否有新的消息,如果有的话就回调相应的消息处理函数,否则就一直等待着(阻塞)。Looper中还有个特殊的概念——ThreadLocal,它并不是线程,它的作用是在每个线程中存储数据。
我们直接上源码开撸:
首先看一下它的构造函数:

1
2
3
4
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

我们可以看到在构造方法中它会创建一个MessageQueue即消息队列,然后通过变量mThread将当前线程保存起来。
我们知道,Handler的运行需要Looper,没有Looper的线程会报错,那如何为一个线程创建Looper?Looper.prepare()这个方法就是提供了这个功能:

1
2
3
4
5
6
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));
}

在Looper.prepare()方法中sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量,通过sThreadLocal.set()将一个Looper实例放入了ThreadLocal,中间的if判断则说明了Looper.prepare()方法不能被调用两次,一个线程只能有一个Looper实例。
Looper最重要的一个方法是loop(),只有调用了loop后,消息循环机制才会真正地起作用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
* 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();
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.recycle();
}
}

/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
}

我们一行行看代码,在myLooper()方法中返回了sThreadLocal存储的Looper实例,如果me为null,则抛出异常,也就是说loop()必须在prepare()方法之后运行。接着拿到该looper实例中的mQueue(具体可见Looper的构造函数)。再接下来就进入一个for(;;)死循环,而唯一跳出循环的方式是MessageQueue的next()方法返回了null。紧接着把消息交给msg的target的disptchMessage(msg)方法去处理。而msg的target就是handler对象,下面会进行分析。最后一句msg.recycle()则释放消息占据的资源。

接着我们分析一下MessageQueue.next()什么时候会返回null?我们先看看Looper的quit()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public void quit() {
mQueue.quit(false);
}

public void quitSafely() {
mQueue.quit(true);
}

//MessageQueue的quit(boolean safe)方法

void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}

synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;

if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}

// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}

private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}

private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}

也就是说当Looper的quit方法被调用时,Looper就会调用MessageQueue的quit()或quitSafely()方法来通知消息队列退出,当消息队列被标记为退出状态时,它的next方法就会返回null。

小结
Looper主要作用:

  1. 与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。(具体可见Looper的构造函数)
  2. looper()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage()去处理。