Handler 消息机制

在 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[main,5,main]
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,则回调其 handleMessage 方法对消息进行处理,否则则调用 public void handleMessage(@NonNull Message msg) 方法,由子类 Override 该方法对信息进行处理

PS. 如果 Handler.Callback 的 handleMessage() 方法返回了 true,则代表不再需要对该消息进行处理,否则还会调用 handleMessage 方法对该消息进行处理

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
56
57
58
59
60
61
62
63
64
65
66
67
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()) {
//如果该 message 已经在使用了,抛出异常
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();
//将执行时间赋值给 when
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//如果 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.
//将消息插入到队列中间
//通常来说,我们不需要唤醒事件队列,除非在队列的头是一个屏障消息(target == null),并且要插入的消息是队列中的第一个异步消息
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//
//下面是个死循环,直到找到队列中的消息的执行时间(when 字段)小于该插入消息的时间的,插在其前面,如果没有,则插入到队列最后面
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
//如果 p 是异步消息,说明要插入的消息不是第一个异步消息(因为走到这里来说明要插入的消息是插入到了 p 后面了)所以不需要唤醒
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 并进行返回
如果在某次循环中获取不到 msg ,则会去处理 idle handler ,处理完后将 pendingIdleHandlerCount 置为 0,保证下次循环不处理 idle 了

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
127
128
129
130
131
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.
//如果消息循环已经退出并且被处理了,则会在这里 return
//这种情况可能发生在如果应用尝试在退出后重启 looper
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();
}

//这里会进行休眠,相对应的 nativeWait() 会进行唤醒
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(可能是消息屏障后的第一个消息,也可能是原本消息队列中的头,即 mMessage)
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 不为 null,说明 msg 为需要处理的异步消息,被提前取出来处理了
//而 prevMsg 就是该消息的前一个消息,现在将 msg.next 链接到 prevMsg.next 的后面,(其实就相当于删除了中间的 msg)
//此时的 mMessage 还是原来的 mMessage,下次进入 next() 方法 mMessage 还是原来的那个
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.

//如果第一次闲置,则会获取 idleHandlers 的数量
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//如果 idleHandler 数量小于0,则跳过后面的 idleHandler 的逻辑,继续下一个 next() 循环
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)];
}
//将外部添加进来的 mIdleHandlers 列表拷贝到 mPendingIdleHandlers 数组中
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}

// Run the idle handlers.
// We only ever reach this code block during the first iteration.
//运行 idle Handler 的逻辑,这里只可能在第一次 for 循环的迭代时被执行
//(因为后面将 pendingIdleHandlerCount 置为 0了,所以导致后续 pendingIdleHandlerCount 一直为 0)
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 {
//IdleHandler 各自处理逻辑,并返回是否需要被移除
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}

if (!keep) {
//如果返回需要被移除,则移除该 idleHandler
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}

// Reset the idle handler count to 0 so we do not run them again.
//将待办的闲置 IdleHandler 的数量置为 0
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 中找到异步的消息并返回

但是由上文可知,在调用 enqueueMessage 方法后,会将 msg 的 target 赋值为当前的 Handler 导致我们的上面想插入一个 target == null 的 Message 的想法不成立

并且 MessageQueue#postSyncBarrier() 方法并不开放给开发者使用,所以要开启同步屏障,我们只能通过反射该方法进行调用

而如果直接使用 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,则会将该消息插入到消息队列的队头

何时会插入同步屏障

在绘制流程中,ViewRootImpl 的 requestLayout 方法中会调用 postSyncBarrier 方法开启同步屏障优先绘制 UI

7. 举例

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

private val normalHandler by lazy {
Handler(Looper.getMainLooper(),object : Handler.Callback{
override fun handleMessage(msg: Message): Boolean {
Log.d(TAG,"普通 Handler 回调 msg is $msg")
return true
}
})
}

private val asyncHandler by lazy {
Handler.createAsync(Looper.getMainLooper(),object : Handler.Callback{
override fun handleMessage(msg: Message): Boolean {
Log.d(TAG,"异步 Handler 回调 msg is $msg")
return true
}
})
}
private fun testSendNormalMessage(){
Log.d(TAG,"普通 Handler sendMessage start")
normalHandler.sendMessage(Message().apply { obj = "普通 Handler 调用 sendMessage 发送的消息" })
Log.d(TAG,"普通 Handler sendMessage end")

Log.d(TAG,"异步 Handler sendMessageAtFrontOfQueue start")
asyncHandler.sendMessageAtFrontOfQueue(Message().apply { obj = "异步 Handler 调用 sendMessageAtFrontOfQueue 发送的消息" })
Log.d(TAG,"异步 Handler sendMessageAtFrontOfQueue end")

Log.d(TAG,"异步 Handler sendMessage start")
asyncHandler.sendMessage(Message().apply { obj = "异步 Handler 调用 sendMessage 发送的消息" })
Log.d(TAG,"异步 Handler sendMessage end")

Log.d(TAG,"普通 Handler sendMessageAtFrontOfQueue start")
normalHandler.sendMessageAtFrontOfQueue(Message().apply { obj = "普通 Handler 调用 sendMessageAtFrontOfQueue 发送的消息" })
Log.d(TAG,"普通 Handler sendMessageAtFrontOfQueue end")
}

打印日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
普通 Handler sendMessage start
普通 Handler sendMessage end
异步 Handler sendMessageAtFrontOfQueue start
异步 Handler sendMessageAtFrontOfQueue end
异步 Handler sendMessage start
异步 Handler sendMessage end
普通 Handler sendMessageAtFrontOfQueue start
普通 Handler sendMessageAtFrontOfQueue end
普通 Handler 回调 msg is { when=-4d23h57m46s154ms what=0 obj=同步 Handler 调用 sendMessageAtFrontOfQueue 发送的消息 target=android.os.Handler }
异步 Handler 回调 msg is { when=-4d23h57m46s154ms what=0 obj=异步 Handler 调用 sendMessageAtFrontOfQueue 发送的消息 target=android.os.Handler }
普通 Handler 回调 msg is { when=-13ms what=0 obj=普通 Handler 调用 sendMessage 发送的消息 target=android.os.Handler }
异步 Handler 回调 msg is { when=-13ms what=0 obj=异步 Handler 调用 sendMessage 发送的消息 target=android.os.Handler }

此时的 asyncHandler 并不能将其消息插入到消息队列的最前面,而是取决于其插入的顺序,后调用 sendMessageAtFrontOfQueue 的消息后插入到前面去

如果要使得 asyncHandler 插入的消息能优先处理,则需要同步屏障起作用,我们先通过反射开启一下同步屏障,再在使用完毕后关闭同步屏障

1
2
3
4
5
6
7
8
9
10
11
12
private var barrierToken = 0

fun startReflectPostSyncBarrier(looper: Looper){
val method: Method = MessageQueue::class.java.getDeclaredMethod("postSyncBarrier")
barrierToken = method.invoke(looper.queue) as Int
}

fun stopReflectPostSyncBarrier(looper: Looper){
val method = MessageQueue::class.java
.getDeclaredMethod("removeSyncBarrier", Int::class.javaPrimitiveType)
method.invoke(looper.queue, barrierToken)
}

在调用 testSendNormalMessage 方法前先调用 startReflectPostSyncBarrier 方法,建立起同步屏障

日志打印如下:

1
2
3
4
5
6
7
8
9
10
11
普通 Handler sendMessage start
普通 Handler sendMessage end
异步 Handler sendMessageAtFrontOfQueue start
异步 Handler sendMessageAtFrontOfQueue end
异步 Handler sendMessage start
异步 Handler sendMessage end
普通 Handler sendMessageAtFrontOfQueue start
普通 Handler sendMessageAtFrontOfQueue end
普通 Handler 回调 msg is { when=-5d0h9m7s27ms what=0 obj=普通 Handler 调用 sendMessageAtFrontOfQueue 发送的消息 target=android.os.Handler }
异步 Handler 回调 msg is { when=-5d0h9m7s27ms what=0 obj=异步 Handler 调用 sendMessageAtFrontOfQueue 发送的消息 target=android.os.Handler }
异步 Handler 回调 msg is { when=-11ms what=0 obj=异步 Handler 调用 sendMessage 发送的消息 target=android.os.Handler }

startReflectPostSyncBarrier() 方法建立起了同步屏障,即往消息队列中插入了一条 target = null 的消息,且 msg.when 的值为当前时间
由于普通(同步)的 Handler 将插入了一条消息到消息队列最前面,when == 0,所以导致会先处理该普通消息,然后再遇到了 target == null 的消息,则开始遍历异步消息进行处理,如果不调用 android.os.MessageQueue#removeSyncBarrier 方法将 target == null 的消息移除掉,则该 MessageQueue 中的同步消息就一直无法得到处理

总结

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

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

Handler 消息机制.png

本文参考:

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

作者

PPTing

发布于

2021-04-21

更新于

2023-11-17

许可协议

评论