在 Android 中,使用 Handler 主要用于不同线程间的通信

本文基于 Target 30 的 Android 源码进行分析

先来看几个类

概念

Handler : 消息处理类,用来发送和处理 Message

Looper : 循环器,将消息发送给 Handler 进行处理

MessageQueue : 消息队列

Message : 消息

简介

Handler 消息机制 UML 类图

Handler 消息机制 UML

消息机制类比图

Handler 消息机制.png

1. Example

先来看个例子

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
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val button = Button(context)
setContentView(view, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT))
button.setOnClickListener {
thread {
Log.d("MainActivity","thread is ${Thread.currentThread().name}")
Looper.prepare()
val looper = Looper.myLooper()!!
val handler = MyHandler(looper)

val message = Message().apply {
what = 1
obj = "message"
}
handler.sendMessage(message)

Looper.loop()
}
}
}

class MyHandler(looper: Looper) : Handler(looper){
/**
* Subclasses must implement this to receive messages.
*/
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
//①
Log.d("MyHandler","thread is ${Thread.currentThread().name} msg.what is ${msg.what}")
}
}
}

日志如下:

1
2
MainActivity: thread is Thread-3
MyHandler: thread is Thread-3 msg.what is 1

每点击一次按钮,会创建一个新的线程,并在该线程中通过 handler 将消息分发给 MyHandler#handleMessage 进行处理,在这个例子中,MyHandler#handleMessagethread{}

是同一个线程,并没有跨线程通信,所以对于线程间的通信来说,这个例子并没有什么意义,只是大概告知一下 Handler 的使用,但事实上,在 Android 的主线程(即 UI 线程、Main Thread) 就是通过这种方式对消息进行分发的

详见 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
24
25
26
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

//......忽略一些细节

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

在进行跨线程通信中,例如在子线程中做完耗时任务后,通知主线程更新UI

我们会在子线程中获取到主线程的 Looper (Looper.getMainLooper()) 后,使用一个持有该 Looper 的 Handler 发送一个消息,而后在该 Handler 的 handlerMessage 方法中接收该消息进行更新UI 等操作

eg. 将上述例子中的 Handler 的 Looper 替换,则在

1
2
3
4
5
6
7
8
9
10
11
thread {
Log.d("MainActivity","thread is ${Thread.currentThread().name}")
val handler = MyHandler(Looper.getMainLooper())

val message = Message().apply {
what = 1
obj = "message"
}
handler.sendMessage(message)

}

日志如下:

  • 可见已经切换到主线程中接收到信息了
1
2
MainActivity: thread is Thread-3
MyHandler: thread is main msg.what is 1

通过以上的例子,我们来分析 Android 中的消息机制到底是如何运作的

2. Looper

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

可见,第一次调用 prepare() 方法时会在当前线程中 new 一个 Looper 对象,并保存在该线程的 mThreadLocal 中

若在同个线程中多次调用 prepare 方法,则会走到 if 的 case 中抛出异常

因此保证在同一个线程中只有一个 Looper 对象存在

2.2 myLooper()

获取当前线程 looper 的方法

1
2
3
public static @Nullable Looper myLooper() {
returnsThreadLocal.get();
}

即返回在 prepare() 方法中保存的 Looper 对象

2.3 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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
public static void loop() {
final Looper me = myLooper();
if (me == null) {
//Looper 还没有初始化,则抛出异常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}

me.mInLoop = true;
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();

// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);

boolean slowDeliveryDetected = false;

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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;

final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;

if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}

final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
//消息不为空,分发给消息对应的 target(即 Handler)去处理
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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();
}
}

可见 loop() 方法中是一个 for(;;) 的死循环

大概分成:

  1. 从当前 looper 对应的 messageQueue 中通过 ,mQueue.next() 方法获取下一个 Message[该方法可能会阻塞,详见 5.2 MessageQueue#next()]
  2. 分发给 Message 对应的 target (即 Handler)[ target 赋值见 4.3.1 android.os.Handler#enqueueMessage] 交由 Handler 的 dispatchMessage [见 4.4 android.os.Handler#dispatchMessage] 去处理
  3. 最后再将 Message 进行回收利用[见 3.3 Message#recycleUnchecked()]

接着不断的重复这个过程

由于 loop() 方法中进行的是死循环,所以在 loop() 方法后的代码是不会被调用到的

loop() 方法中,获取下一条消息的方法 messageQueue.next() 是阻塞的,所以当 messageQueue 中没有新的消息了,loop() 会阻塞住[见 MessageQueue#next()],所以不会造成 CPU 的高消耗

3. Message

Message 是整个消息分享机制中的「信使」,将信息从 A 带给 B

Message 类中包含以下一些字段

成员变量 类型 解释
what Int 开发者自定义的一个 code 字段,用来标识是什么类型的消息后续进行处理
arg1 Int arg1 和 arg2 都是用来存储数据的备选方案,如果需要存储的数据不多而不想使用 data 的话,则可以考虑使用该字段
arg2 Int 同上
data Bundle 用于在 Message 中存储自定义数据
obj Object 用于发送给接受者的任意对象,注意,如果使用 Messager 在跨进程中的发送数据时,只能发送 framework 中的 Parcelable 对象,且不能为 null,不能发送自定义的 Parcelable 对象,要发送自定义的 Parcelable 对象应该使用 setData 方法
when Long Message 的目标发送处理时间,这个时间是基于系统开机时间计算的SystemClock#uptimeMillis
target Handler 用于处理该消息的 Handler
callback Runable Runnable 类型
next Message Message 的下一个 Message,可见 Message 是一个单链表的数据结构
sPool Message 用来做 Message 的缓存池

3.1 obtain()

从 Message 缓存池中获取一个 Message 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}

sPoolSync 是一个锁对象,用来在对 Message 进行复用回收时候进行线程同步处理,避免多线程访问时的内存数据共享错误

首先通过上面的 Message#next 我们知道 Message 是一个单链表的数据结构

obtain() 方法判断当 sPool 不为空时,即已经有被创建出来的 Message 对象了,则赋值给 m 并将 m.next 赋值给 sPool,即将单链表中的第一个节点取出,并将第二个节点作为 sPool ,即剩下的链表继续作为缓存池,同时将缓存池的数量减一。最后将需要返回的 m 的 next 赋值为 null,清除 flag 等并返回 m

3.2 recycler()

1
2
3
4
5
6
7
8
9
10
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}

先检查该 Message 是否还在被使用,如果还在被使用则抛出异常,否则对该 Message 进行回收,详见 3.3 recyclerUnchecked()

3.3 recyclerUnchecked()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;

synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}

这个方法就很简单了,将 Message 的成员变量都还原为初始化状态,接着将该 Message 插入到缓存池链表的头部,并更新缓存池的数量

4. Handler

4.1 构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public Handler(@Nullable Callback callback, boolean async) {
//...忽略...

mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
1
2
3
4
5
6
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

两个构造方法的区别在于是否有 Looper 参数

如果没有传入 Looper 参数,则会调用 Looper.myLooper() 方法获取当前线程中的 Looper 赋值给 mLooper ,如果当前线程的 Looper 还未初始化,则抛出异常

4.2 sendMessageDelayed(Message msg , long updateMillis)

Handler 中的 post(Runnable r)postDelayed(@NonNull Runnable r, long delayMillis) 等等方法最终都会调用 boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) 方法,我们先来看如何将 Runnable 转为 Message 的方法,再接着看 sendMessageAtTime

1
2
3
4
5
6
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

这里有个值得注意点是,传入 sendMessageAtTime 方法中的第二个参数是用的是 SystemClock.uptimeMillis() 即当前距离开机的时间,使用这个时间就不会因为机器的时间戳变化而导致不准确的问题

4.2.1 getPostMessage(Runnable r)

1
2
3
4
5
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

即从缓存池中获取一个 Message 对象,并将其 callback 设置为传入的 Runnable 对象

至于这个 callback 的作用,详见 4.4 dispatchMessage

4.3 sendMessageAtTime(Message msg, long uptimeMillis)

1
2
3
4
5
6
7
8
9
10
public boolean sendMessageAtTime(@NonNull 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);
}

sendMessageAtTime 方法会先判断当前 Handler 中的消息队列 mQueue 是否为空,如果为空则抛出异常并打印日志返回

否则调用 enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) 方法将该 Message 消息对象放入消息队列中

4.3.1 enqueueMessage(MessageQueue queue,Message msg,long updateMillis)

将 Message 的 target 设置为当前的 Handler 对象,并压入 MessageQueue 队列中

1
2
3
4
5
6
7
8
9
10
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();

if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

MessageQueue 的 enqueueMessage 方法详见 5.1 MessageQueue#enqueueMessage()

4.4 dispatchMessage

在 4.3.1 方法中,enqueueMessage 方法将 Message 压入 MessageQueue 后,会被 2.3 Looper#loop() 不停获取队列中的 Message 并交由其 target(即 Handler) 处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

首先会判断 msg 的 callback 是否为空,如果不为空,则调用该 callback 的 run 方法

由 3. Message 中我们知道 callback 就是一个 Runnable 对象

如果 callback 为空,则判断 Handler 的成员变量 mCallback 是否为空,如果不为空,则调用其接口方法 boolean andlerMessage(Message msg) ,由实现了该 Handler.Callback 接口的子类自行处理

如果 Handler 的子类没有实现 Handler.Callback 接口,则调用 public void handleMessage(@NonNull Message msg) 方法,由子类 Override 该方法对信息进行处理

5. MessageQueue

MessageQueue 是一个消息队列

5.1 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
48
49
50
51
52
53
54
55
boolean enqueueMessage(Message msg, long when) {
//如果 Message 没有设置 target 则抛出异常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}

synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}

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

参数 when 是指需要执行的时间戳(用距离机器开机的时间来计算)

  • 先判断 Message 的 target 是否为空,为空则抛出异常
  • 再判断 Message 是否被使用了,如果该 Message 已经被使用了则抛出异常
  • 再判断是否已经退出了,如果已经退出了则将 Message 回收并返回 false
  • 以上检查无异常后,将 Message 标记为在使用

着重看一下后面的一段代码

判断

  1. p 是否为空(即当前的消息队列为空)
  2. when == 0(即该消息需要立即执行)
  3. when< p.when(即该消息的执行时间早于消息队列的第一条消息的执行时间)

如果满足以上任何一个条件,则将该 Message 插入到队列的头部

否则对 消息队列进行遍历,直到将该消息插入到执行时间都比该消息小的消息后面

可见,MessageQueue 是按照 Message.when 对消息进行排序的,链表中的 Message 按照 when 的大小排序

5.2 next()

next() 内部有一个 for(;;) 的死循环,一直从 Message 链表中获取符合条件的 Message 并进行返回

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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;
}

int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}

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) {
//当前消息队列不为空,且 target 为 null,注意这里的 target 为 null
//则从消息队列中找到异步的 Message
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());//msg 不为空且为同步的消息,则继续循环直到找到异步的消息
}
//此时 msg 就是第一个需要进行处理的 Message
if (msg != null) {
if (now < msg.when) {
//如果当前时间还没到消息需要处理的时间,则设置一个延时时间,这里不会 return Message,如果还没到消息需要处理的时间,所以 for 循环会一直进入这个 case 导致阻塞在这里
// 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 (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}

// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}

// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}

if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}

// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler

boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}

if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}

// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;

// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}

5.3 quit()

将 mQuitting 标志位置为 true,后续 MessageQueue 的 next() 方法等地方 使用该字段做判断后进行处理

6. 消息同步屏障

同步屏障,顾名思义,将同步的消息给挡住,优先处理非同步(即异步)的消息

从以上我们 MessageQueue#next() 中,我们知道从 MessageQueue 中获取队列中的消息时,会优先获取异步的消息(msg.isAsynchronous() == true)进行处理

6.1 如何开启同步屏障

MessageQueue#postSyncBarrier(long when) 方法中,会在 MessageQueue 消息队列的队头插入一个 message(这个 Message 的 target 为 null)

这个 message 仅仅作为一个标志存在,并不用来分发事件,
当 looper 在进行循环🔄时,调用 queue.next() 方法,当遇到 message.target == null ,则会从 MessageQueue 中找到异步的消息并返回

PS. MessageQueue#postSyncBarrier() 方法并不开放给开发者使用,所以要开启同步屏障,我们可以自己往 MessageQueue 中插入一个 message.target == null 的 message

而如果直接使用 Handler.sendMessage() 等方法插入消息,会根据时间顺序插入到队列中,如果要立即生效,则需要将消息插入队列队头,如何将消息插入到队列头部,详见 * 6.3 如何将消息插入队列头部 *

6.2 如何插入异步消息

  1. 构造异步 Handler
    4.3.1 enqueueMessage 中可以看到,当 mAsynchronous 字段为 true 时候,会将 message 设置为异步消息
    所以只需要在构造 Handler 的时候,将构造方法中的 async 字段设置为 true 即可,则后续所有的消息都会是异步的消息

  2. 构造异步的 Message

在构造 Message 时,通过 setAsynchronous(boolean async) 方法设置该消息为异步消息即可

6.3 如何将消息插入队列头部

由上述分析我们知道通过 Handler#enqueMessage() 方法将消息插入 MessageQueue 中,会根据 message.when 由小到大插入到队列中,如果需要将消息插入到队头,那么应该使得 when 为 0,但 when 这个参数也是不支持开发者修改的

但是 Handler 中提供了一个 Handler#postAtFrontOfQueue (最终调用 Handler#sendMessageAtFrontOfQueue) 方法。这个方法在将消息插入队列前,会将 when 设置为0,则会将该消息插入到消息队列的队头

总结

用以下一幅图来总结 Android 中的消息分发机制

Looper 像是一个发动机,不停地将生产者产生的 Message 从传送带(MessageQueue) 中分发给消费者去处理

Handler 消息机制.png

本文参考:

Android消息机制1-Handler(Java层)