Message、Handler、MessageQueue、Looper关系

背景

最近手头上有一些面试的任务,这个问题就是我比较常问面试者的问题,Android设计这样一个东西,理解源码后才能理解,才能更好的帮助我们进行更高层次的开发。所以就自己整理下,把阅读源码的思路记录下来。

类图结构

用一句话概括:

Handler把Message放入到一个Looper的MessageQueue中,我们用Looper.prepare() 准备一个当前线程的Looper,然后通过Looper.loop()进行消息循环,Looper.loop()方法中会通过MessageQueue.next()来进行消息的获取,如果没有消息就会阻塞

Message

Message实现了Parcelable接口,所以Message是可以进行跨进程传输的,但是需要注意obj,这个obj也必须需要实现Parcelable,否则会抛异常。

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
public void writeToParcel(Parcel dest, int flags) {
if (callback != null) {
throw new RuntimeException(
"Can't marshal callbacks across processes.");
}
dest.writeInt(what);
dest.writeInt(arg1);
dest.writeInt(arg2);
if (obj != null) {
try {
Parcelable p = (Parcelable)obj;
dest.writeInt(1);
dest.writeParcelable(p, flags);
} catch (ClassCastException e) {
throw new RuntimeException(
"Can't marshal non-Parcelable objects across processes.");
}
} else {
dest.writeInt(0);
}
dest.writeLong(when);
dest.writeBundle(data);
Messenger.writeMessengerOrNullToParcel(replyTo, dest);
dest.writeInt(sendingUid);
}

Message 中有一个Messager 可以接收方用来回信的,比较少用。 Message本身功能很简单,维护了一个MAX_POOL_SIZE=50的链表池,用来复用的,这是每个进程的,也就是每个进行都维护了长度为50的池子。

Handler

Handler的结构是很简单的,基本也没有做啥,都是一些封装,屏蔽了MessageQueue的细节。提供了post和send Message,同时提供了Message obtain Handler,Message的target就是Handler,这个Message的target最终是有Looper拿到Message后,通过target来调用Handler的 dispatchMessage(Message msg),Handler中有一个mAsynchronous变量,这个我们最后来说说异步消息是干什么的

我们看看这个方法:

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

/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}

/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}


/**
* 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);
}
}

这个方法比较有趣:

  1. 如果构造的Message中设置了Runnable的callback,会直接调用run方法,这个时候并不会执行handleMessage
  2. mCallback 是一个接口,这个接口是通过Handler的构造方法来传入,如果设置这个方法并且接口实现返回True,那Handler自身的方法handleMessage是不会掉用的
  3. 如果Message的callback 没有设置 并且 mCallback为null,或者mCallback的handleMessage返回false,Handler本身的handleMessage才会执行

所以Handler只是一个包装者,最后通过当前的线程的Looper把Message交给接受者处理。

接下来我们先看看Looper

Looper

从类图的结构来看,Looper向外部暴露的方法都是静态的,那么怎么为不同的线程提供Looper环境呢?

关键的是在ThreadLocal,为每个线程提供一个隔离的环境。

当在不同的线程开启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
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);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}

...

msg.recycleUnchecked();
}
}

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

从源码来看,loop这个静态方法会调用myLooper,这个方法会取出当前线程的Looper,然后loop方法通过looper获取到MessageQueue,通过MessageQueue的next来获取消息Message,如果没有消息就会block,如果有消息,然后会调用

1
2
msg.target.dispatchMessage(msg);

target 是Message绑定的Handler,然后调用Handler中的dispathMessage,这样就进入到消息的分发。

Looper逻辑本身也比较简单:

  1. 通过Looper.prepare()来准备当前线程的Looper,保存在sThreadLocal中,如下的源码:
1
2
3
4
5
6
7
8
9
10
11
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));
}

  1. 然后通过Looper.loop()来进行消息循环,loop方法中会获取当前线程的Looper,所以Handler是一定需要Looper。平常我们在主线程可以直接创建一个不带Looper的Handler是因为主线程已经帮我们创建好了Looper,我们看看ActivityThread中的main方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 public static void main(String[] args) {
...

Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);

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");
}

Looper 中

1
2
3
4
5
6
7
8
9
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

所以我们在ActivityThread中已经在主线程中创建了MainLooper了。

MessageQueue

先提一个问题:发送Handler是如何做到发送延时消息线程有不阻塞的呢?

从类图中,我们可以看到MessageQueue中有一个Message

1
2
Message mMessages;

我们能够知道MessageQueue是通过Message的next变量来进行链式结构,我们看看Message怎么加入到MessageQueue的,看看源码的方法

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
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) { //不是异步消息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) {
// 如果上一个Message为null,或者 延迟时间为0,或者当前消息的延时小于上一个消息,应该更早的被执行
// New head, wake up the event queue if blocked.
msg.next = p; // msg应该被放置到头部,上一个消息在该消息的后面
mMessages = msg;
needWake = mBlocked;
} else {

// 这段逻辑就是找到msg链式结构的合适位置
// 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;
}

Handler中最终都是通过这个方法来入队消息,这个方法主要做的事情就是找到当前入队msg在链式结构的合适位置,如果当前msg位于对头并且被阻塞就唤醒。这个是将消息放入链式中,然后通过条件要不要唤醒,如果唤醒Looper中的loop就会取消息,这个时候就会到MessageQueue的next方法来拿消息,如下源码:

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
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// 用来保存注册到消息队列中的空闲消息处理器(IdleHandler)的个数,当消息队列
// 没有新消息需要处理时,不是马上进入睡眠等待状态,而是先调用已注册的IdleHandler
// 对象的queueIdle(...)函数,以便有机会进行空闲处理。
int pendingIdleHandlerCount = -1; // -1 only during first iteration
// 若没有新消息待处理,线程睡眠等待的时间。
// 0表示不要进入睡眠等待状态;-1表示若无新消息,则永久睡眠等待
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}

//epoll 阻塞
nativePollOnce(ptr, 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 {
//当前消息指向下一个队头消息,这个无限的循环就是不停的获取消息,而我们消息在入队的时候已经插入到合适的位置,所以这里只要挨个遍历就好,如果发现当前时间小于消息要被执行的时间就调用nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}

...
...
}
}

后续继续分析如下的问题:

  1. 异步消息的作用,解决了什么问题
  2. Looper会无限循环,没有消息会休眠,为啥不会ANR,其机制和原理是怎么样的,
  3. Android主线程是何时创建的