0%

Handler机制分析

Handler机制组成元素之间的关系

Handler机制主要有Handler、MessageQueue、Message、Looper几个元素构成。
它们之间的关系是:

  • 一个线程只有一个Looper实例
  • Looper中持有队列mQueue
  • Handler持有队列mQueue和Looper对象。在构造Handler实例时如果没有Looper入参,那就默认使用当前线程的Looper,ThreadLocal<Looper>.get()
  • Message中持有handler和next message
  • MessageQueue中持有当前message
  • 生产消息:Handler发送的信息通过MessageQueue.enqueueMessage将消息入队
  • 消费消息:Looper.loopOnce中将MessageQueue的消息取出,调用Message.target.dispatchMessage,target属性就标记了消息最终交给哪个Handler处理,所以这里的含义是在生产Msg的Handler中执行处理逻辑;如果MessageQueue信息为空,就会执行被挂起的IdleHandler。

dispatchMessage方法分析

dispatchMessage方法关系到消息队列中消息所对应的处理逻辑最终在哪如何被处理

title:android.os.Handle.dispatchMessage
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);
}
}

方法有三个逻辑分支,都是处理MessageQueue抛出的Msg:

  1. msg.callback不为空
    message.callback.run() Message的callback成员是一个Runnable对象
  2. Handler.mCallback不为空
    由Handler.Callback的接口实现来处理
  3. msg.callback和Handler.mCallback都为空
    由Handler.handleMessage方法处理,子类没重写则默认不处理

Handler处理并发实现

title:android.os.MessageQueue.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
boolean enqueueMessage(Message msg, long when) {  
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 因为可能在任意对象操作入队,而只会在looper所绑定的线程出队,所以这里加对象锁,保证入队出队操作是线程安全的
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;
}
title:android.os.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
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
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); // 用来检查消息队列中是否有新的消息要处理,当队列为空时,`nativePollOnce` 会使线程等待直到:1. 有新消息到达。2. 被唤醒去处理其他任务(例如,定时事件、输入事件等)。3. 明确使用 `wakeUp()` 方法唤醒。

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 (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;
}
}
title:android.os.Looper
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
132
133
134
private static boolean loopOnce(final Looper me,  
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}

// 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 {
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 (me.mSlowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
me.mSlowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
me.mSlowDeliveryDetected = 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();

return true;
}

/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
@SuppressWarnings("AndroidFrameworkBinderIdentity")
public static void loop() {
final Looper me = myLooper();
if (me == null) {
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;

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

me.mSlowDeliveryDetected = false;

for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}

Handler的并发处理其实就是消息入队和出队被处理的过程

  1. 可以在任意线程将消息入队,具体线程由Handlder.sendMsg的方法栈决定
  2. 只会在Looper.loop方法中执行出队,而Looper.loop只会在指定的一个线程中执行的,也就是消息最终被处理的线程
  3. 可以看出入队和出队可能是在不同的线程中执行的,在MessageQueue中通过对象锁来保证线程安全

Hanlder与ANR的关系

消息阻塞机制

当主线程阻塞超过5s之后,就会触发ANR;前面我们知道,在Looper开启死循环取消息的时候,如果消息队列中没有消息的时候,就可能会被block,调用了nativePollOnce,那么为什么没有阻塞主线程呢?

其实应该把这分为两件事来看,looper.loop是用来处理消息,当没有消息的时候,主线程就休息了,不需要干任何事;像input事件,其实就是一个Message,当它加入到消息队列的时候,会调用nativeWake唤醒主线程,主线程来处理这个消息,只有处理这个消息超时,才会发生ANR,而不是死循环会导致ANR。

案例分析

title:"线程休眠"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
"main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 flags=1 obj=0x7185b6a8 self=0xb400007375b4bbe0
| sysTid=3433 nice=0 cgrp=default sched=0/0 handle=0x749c9844f8
| state=S schedstat=( 800801640 66783841 881 ) utm=60 stm=19 core=0 HZ=100
| stack=0x7fc20cb000-0x7fc20cd000 stackSize=8192KB
| held mutexes=
native: #00 pc 000000000009ca68 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8)
native: #01 pc 0000000000019d88 /system/lib64/libutils.so (android::Looper::pollInner(int)+184)
native: #02 pc 0000000000019c68 /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112)
native: #03 pc 0000000000112194 /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
at android.os.MessageQueue.nativePollOnce(Native method)
at android.os.MessageQueue.next(MessageQueue.java:335)
at android.os.Looper.loop(Looper.java:183)
at android.app.ActivityThread.main(ActivityThread.java:7723)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:997)

在我们分析ANR日志时,经常会看到这样表现,结合上面我们对于Handler的了解,这个时候其实就是没有消息了,我们看已经调用了nativePollOnce方法,此时主线程就休眠了,等待下一个消息到来。

title:"ANR堆栈"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
"main" prio=5 tid=1 Blocked
| group="main" sCount=1 dsCount=0 flags=1 obj=0x7185b6a8 self=0xb400007375b4bbe0
| sysTid=3906 nice=-10 cgrp=default sched=0/0 handle=0x749c9844f8
| state=S schedstat=( 2591708189 61276010 2414 ) utm=220 stm=38 core=5 HZ=100
| stack=0x7fc20cb000-0x7fc20cd000 stackSize=8192KB
| held mutexes=
// ......
- waiting to lock <0x0167ghe6d> (a java.lang.Object) held by thread 5
// ...... 方法调用
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7723)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:997)

在这段堆栈中,我们看到主线程已经是出问题了,处于Blocked的状态,那么在Handler调用dispatchMessage方法的时候,是调用了handleCallback,说明此时是调用了post方法,在post方法中,主线程一直想要获取其他线程持有的一把锁,导致了超时产生了ANR。