Fork me on GitHub

Android Framework 专项 - Handler(一)

问题

1
2
The formulation of the problem is often more essential than its solution, which may be merely a matter of mathematical or experimental skill.
― Albert Einstein

Q:一个线程有几个 Handler?

Q: 线程间的通信的原理是怎样的?

Q: Handler 内存泄漏的原因?为什么其他的内部类没有说过这个问题?

Q: 为何主线程可以 new Handler ?如果想要在子线程中 new Handler 要做些什么准备?

Q: 子线程中维护的 Looper,消息队列无消息的时候的处理方案是什么?有什么用?

Q: 既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部时如何确保线程安全的?

Q: Looper 死循环为什么不会导致应用卡死

Q: 为什么主线程不需要自己创建和管理消息循环

Handler 在 Android 中的应用

1
Handler 是针对 Android 系统中与 UI 线程通信而专门设计的多线程通信机制

Retorfit,eventbus,rxjava,Looper

Handler 源代码分析

子线程 发送 MSG

​ android.os.Handler#sendMessage ->
​ android.os.Handler#sendMessageDelayed ->
​ android.os.Handler#sendMessageAtTime ->
​ android.os.Handler#enqueueMessage ->
​ android.os.MessageQueue#enqueueMessage ->
​ android.os.Looper#loop

android.os.Handler#sendMessage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    /**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
/**
* 在所有挂起的消息之后将消息推送到消息队列的末尾
* 当前时间之前。它将在 {@link #handleMessage} 中收到,
* 在附加到该处理程序的线程中。
*
* @return 如果消息成功放入则返回 true
* 消息队列。失败时返回 false,通常是因为
* 处理消息队列的 looper 正在退出。
*/
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
android.os.Handler#sendMessageDelayed
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
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
/**
* 将一条消息放入消息队列中,位于所有挂起的消息之后
* 之前(当前时间+delayMillis)。您将在以下时间收到它:
* {@link #handleMessage},在附加到该处理程序的线程中。
*
* @return 如果消息成功放入则返回 true
* 消息队列。失败时返回 false,通常是因为
* 处理消息队列的 looper 正在退出。请注意,一个
* true 的结果并不意味着该消息将被处理 -- 如果
* Looper 在消息发送之前退出
* 发生则消息将被丢弃。
*/
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
android.os.Handler#sendMessageAtTime
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
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
/**
* 将消息排队到消息队列中,在绝对时间(以毫秒为单位)<var>uptimeMillis</var>之后的所有挂起消息之后。
* <b>时间基准是 {@link android.os.SystemClock#uptimeMillis}。</b>
* 在深度睡眠期间花费的时间将会额外延迟执行。
* 您将在{@link #handleMessage}中接收它,该方法会在与此处理程序连接的线程中执行。
*
* @param uptimeMillis 消息应该传递的绝对时间,使用{@link android.os.SystemClock#uptimeMillis}作为时间基准。
*
* @return 如果消息成功放置到消息队列中,则返回 true 。如果失败,则返回 false ,通常是因为处理消息队列的消息循环正在退出。
* 请注意,返回true并不意味着消息将被处理 - 如果消息传递时间之前消息循环被退出,则消息将被丢弃。
*/
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);
}
android.os.Handler#enqueueMessage
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);
}

到这里,发送流程都没什么好说的

重点:android.os.Handler#handleMessage
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
boolean enqueueMessage(Message msg, long when) {
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;
}
msg.target 是什么

注意:这里 msg.target 就是 msg 持有的 handler 也是 为什么会有内存泄漏风险的原因。

分析代码:

Handler 内存泄漏原因

Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露。 这个泄露是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler持有,这样最终就导致 Activity 泄露。

它的主要作用是将消息按照时间顺序插入到消息队列中,并在必要时唤醒队列以处理这些消息。下面逐步分析代码的功能和逻辑。

  1. 首先,代码对传入的消息 msg 进行一些验证。它确保消息有一个非空的目标(msg.target != null),否则抛出 IllegalArgumentException

  2. 然后,代码在同步块内执行以下操作:

    • 检查消息是否已经在使用中,如果是,则抛出 IllegalStateException。这可能是为了防止重复使用消息,确保每个消息只被处理一次。

    • 检查当前处理器是否正在退出(mQuitting 标志),如果是,就回收消息并返回 false,表示消息未被成功加入队列。

    • 将消息标记为正在使用,并设置消息的触发时间 msg.when 为传入的 when

    • 获取消息队列的头部消息 p

  3. 接下来,代码根据以下条件进行处理:

    • 如果消息队列为空,或者传入的触发时间 when 为 0,或者传入的 when 小于队列头部消息的触发时间 p.when,则将新消息插入到队列头部。如果队列当前被阻塞(mBlocked 标志),则设置需要唤醒队列(needWake = true)。

    • 否则,如果消息需要插入队列中间,则根据条件判断是否需要唤醒队列。具体判断条件是:队列被阻塞、队列头部消息的目标为 null,且传入的消息是异步消息。然后,代码在一个循环中遍历消息队列,找到合适的位置插入新消息。循环会一直迭代,直到找到合适的位置或者遍历完整个队列。

    • 在找到合适的位置后,代码将新消息 msg 插入到队列中。具体做法是,将 msg.next 设置为当前消息 p,然后将前一个消息 prev.next 设置为新消息 msg

  4. 最后,代码根据之前的标志 needWake 来决定是否唤醒队列。如果需要唤醒,则调用本地的 nativeWake 方法(可能是一个底层的本地方法)来唤醒消息队列。

  5. 整个同步块结束后,代码返回 true,表示消息已经成功加入队列。

这段代码的核心功能是在消息队列中插入消息并进行适当的排序,以确保消息按照触发时间顺序进行处理。同时,它还处理了一些异常情况,如消息已经在使用中或者处理器正在退出。唤醒队列的逻辑也在代码的最后部分进行处理。

让我们逐步分析 MSG 插入队列的位置:

第一种插入头部的情况:
1
2
3
4
5
6
7
8
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 {
...
}
  1. 如果消息队列为空,意味着当前没有任何消息在队列中等待处理。在这种情况下,不需要比较触发时间,直接将新消息插入到队列的头部。这会让新消息成为队列的第一个要处理的消息。
  2. 如果传入的触发时间 when 为 0,这可能表示该消息需要尽快处理,因此同样将它插入到队列的头部。
  3. 如果传入的触发时间 when 小于队列头部消息的触发时间 p.when,这意味着新消息应该在队列中位于当前头部消息之前,因此同样将它插入到队列的头部

综合上述情况,无论是队列为空,还是传入的 when 值为 0,或者传入的 when 值小于队列头部消息的触发时间,都会将新消息插入到队列的头部

第二种插入中间的情况:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if(p == null || when == 0 || when < p.when) {
...
} 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;
}
  1. 首先,如果不满足前面提到的条件(消息队列为空,传入的触发时间为0,或传入的触发时间小于队列头部消息的触发时间),那么意味着要插入的新消息 msg 应该位于队列中间。
  2. 在这种情况下,代码会进入一个循环。这个循环的目的是遍历消息队列,以找到合适的位置将新消息 msg 插入队列中。
  3. 循环的条件是 for (;;),这将创建一个无限循环,直到内部的某个条件满足而跳出循环。
  4. 在每次循环迭代中,代码会做以下事情:
    • 将当前消息 p 的引用赋值给 prev,这样可以记录下前一个消息。
    • p 移动到下一个消息(p = p.next)。
  5. 然后,代码检查两个条件:
    • 如果当前消息 p 为空,意味着已经遍历了整个队列,或者队列只有一个消息且当前消息是最后一个消息。
    • 如果传入的触发时间 when 小于队列中当前消息 p 的触发时间 p.when
  6. 如果满足以上任一条件,循环会被中断,这表示找到了合适的位置将新消息 msg 插入到消息队列中。
  7. 在循环的每个迭代中,代码还会检查以下条件:
    • 如果需要唤醒队列(needWake = true),并且当前消息 p 是异步消息(p.isAsynchronous() 返回 true),则将 needWake 设置为 false。这个步骤可能是为了控制是否需要在队列中插入异步消息时唤醒队列。
  8. 一旦找到了合适的位置,代码会执行以下操作:
    • 将新消息 msgnext 指针指向当前消息 p,这相当于将新消息插入到当前消息 p 之前。
    • 将前一个消息 prevnext 指针指向新消息 msg,以确保队列中消息的连接关系正确。
  9. 循环结束后,新消息 msg 已经被插入到队列的合适位置,保持了消息队列的有序性。

总之,这段代码的目的是在消息队列中将新消息插入到适当的位置,以保持消息的时间顺序。在找到合适位置时,会根据一些条件来决定是否需要唤醒队列,这可能与队列的处理机制相关。

此外,如果当前消息队列被阻塞(mBlocked 为 true),则将标志 needWake 设置为 true。这是为了确保在需要唤醒队列以处理消息的情况下,能够在适当的时候执行唤醒操作。唤醒队列的操作可能涉及到一些底层机制,具体如何唤醒可能需要查看更多上下文代码。

总的来说,这段代码逻辑的目的是在特定条件下将新消息插入到消息队列的头部,并根据当前队列的阻塞状态决定是否需要唤醒队列以确保消息能够被及时处理。

主线程 取出 MSG

​ android.os.Looper#loop ->
​ android.os.MessageQueue#next ->
​ android.os.Handler#dispatchMessage ->
​ android.os.Handler#handleMessage

主线程中的 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
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

// Install selective syscall interception
AndroidOs.install();

// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);

Environment.initForCurrentUser();

// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);

// Call per-process mainline module initialization.
initializeMainlineModules();

Process.setArgV0("<pre-initialized>");

Looper.prepareMainLooper();

// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
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");
}

与 handler 相关的关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
...

Looper.prepareMainLooper();
...

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
···

Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}

准备 Looper

1
Looper.prepareMainLooper();

android.os.Looper#prepareMainLooper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. See also: {@link #prepare()}
*
* @deprecated The main looper for your application is created by the Android environment,
* so you should never need to call this function yourself.
*/
@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

android.os.Looper#prepare(boolean)

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

准备 Handler

1
2
3
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

android.app.ActivityThread#getHandler

1
2
3
4
@UnsupportedAppUsage
public Handler getHandler() {
return mH;
}

android.app.ActivityThread#mH

1
2
@UnsupportedAppUsage
final H mH = new H();

开启循环

1
Looper.loop();
重点:android.os.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
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;
}
}
}

这段代码是 Android 框架中 Looper 类的 loop() 方法的实现,负责在当前线程的消息循环中执行消息的分发和处理。

  1. 首先,通过 myLooper() 获取当前线程的 Looper 实例 me,如果没有 Looper 实例则抛出异常,表示未调用 Looper.prepare() 来准备 Looper。
  2. 检查 me.mInLoop,如果当前线程已经在消息循环中,则打印警告日志。
  3. me.mInLoop 标志设置为 true,表示当前线程正在消息循环中。
  4. 使用 Binder.clearCallingIdentity() 来清除当前线程的调用标识,然后再次调用它并将返回的标识 ident 保存下来。
  5. 获取一个可能的系统属性覆盖值 thresholdOverride,用于调整慢分发的阈值。
  6. me.mSlowDeliveryDetected 设置为 false,用于标记是否检测到慢投递。
  7. 进入一个无限循环,不断地执行消息分发和处理。
  8. 在循环中,调用 loopOnce(me, ident, thresholdOverride) 来执行一次消息分发。如果返回值为 false,表示没有更多的消息需要分发,退出循环。

总之,这段代码描述了 Android 中消息循环的核心逻辑。它会在一个无限循环中,不断地从消息队列中获取消息并执行消息分发和处理,直到没有更多的消息需要处理为止。在循环中,还会检查是否有慢分发阈值的系统属性覆盖,并根据需要清除调用标识。如果发现当前线程已经在消息循环中,则会打印警告信息。

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

这段代码是 Android 框架中 Looper 类的一个方法 loopOnce 的实现,主要负责处理消息队列中的消息分发过程。我会逐步解释代码的功能。

  1. 首先,通过 me.mQueue.next() 获取下一个消息,如果没有消息则表示消息队列正在退出,函数返回 false

  2. 如果获取到了消息,则会根据是否设置了日志打印,打印消息的分发信息。

  3. 获取当前的观察者对象和跟踪标签。

  4. 根据阈值覆盖值,更新慢分发和慢投递的阈值。

  5. 判断是否需要记录开始时间和结束时间,如果需要,则记录当前时间作为分发开始时间。

  6. 如果设置了跟踪标签且跟踪标签是启用的,则开始跟踪。

  7. 设置开始分发时间,并获取一个 token 以用于观察者的消息分发起始回调。

  8. 设置当前线程的工作源为消息的工作源 UID。

  9. 使用目标 Handler(Handler 是消息处理的目标)来分发消息。

  10. 如果设置了观察者,通知观察者消息已分发。

  11. 结束分发,记录结束时间。

  12. 如果发生异常,通知观察者分发过程中出现异常,并将异常抛出。

  13. 最终,无论是否发生异常,都会恢复线程的工作源,并根据跟踪标签是否启用,结束跟踪。

  14. 如果设置了慢投递日志,会根据条件判断是否记录慢投递日志。

  15. 如果设置了慢分发日志,会根据条件记录慢分发日志。

  16. 如果设置了日志打印,会打印消息分发完成信息。

  17. 最后,检查分发过程中线程标识是否被更改,如果发生改变,会打印警告日志。

  18. 回收消息对象并返回 true,表示成功分发消息。

总之,这段代码描述了 Android 中消息队列中消息的分发过程,涵盖了消息的跟踪、日志记录、观察者通知等多个方面,确保消息能够按照正确的顺序分发到目标处理程序。

重点: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
106
@UnsupportedAppUsage
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) {
// 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;
}
}

这段代码是一个 Android 消息循环(Message Loop)的一部分,它用于处理消息队列中的消息。消息循环是 Android 应用程序的核心部分,用于处理用户界面事件、线程间通信等。

以下是代码的主要功能和逻辑:

  1. 代码开头使用了一个注解 @UnsupportedAppUsage,这是 Android 的一个标记注解,用于标识不推荐使用的 API 或方法。

  2. next() 方法是一个消息循环的主要函数。它会循环从消息队列中获取消息,然后处理这些消息。

  3. 首先,代码会获取 mPtr,这是一个表示消息队列的指针。如果 ptr 为 0,表示消息队列已经被释放,函数会返回 null

  4. 接下来,进入一个无限循环 for (;;) {...},在循环内部做以下操作:

    • 如果 nextPollTimeoutMillis 不为 0,会刷新待处理的 Binder 命令(一种 Android 进程间通信机制)。

    • 调用 nativePollOnce(ptr, nextPollTimeoutMillis),它是一个本地方法,用于轮询获取下一个消息。

    • 进入同步块,开始处理消息队列中的消息。

    • 首先,获取当前时间 now,然后尝试获取下一个消息。如果消息的目标为 null,表示该消息是一个异步消息(可能由于障碍而被阻塞),会查找下一个异步消息。

    • 如果找到消息并且消息的触发时间在当前时间之后,会设置一个定时器来在消息准备好时唤醒循环。

    • 如果消息已经准备好(触发时间已到),会将消息从队列中移除,并返回该消息。

    • 如果没有找到消息,会将 nextPollTimeoutMillis 设为 -1,表示没有更多消息。

    • 如果 mQuitting 为 true,表示消息队列已经被停止,会调用 dispose() 释放资源,并返回 null

    • 如果是第一次空闲(没有消息待处理),会获取待运行的空闲处理器数量。

    • 如果没有空闲处理器需要运行,会将 mBlocked 设为 true,继续等待。

    • 如果有待运行的空闲处理器,会将它们放入 mPendingIdleHandlers 数组中。

    • 执行空闲处理器的 queueIdle() 方法,该方法用于处理空闲状态。

    • 如果 queueIdle() 返回 false,表示该空闲处理器不再需要运行,会将其从列表中移除。

    • 重置空闲处理器数量为 0,以防止重复运行。

    • 在处理完空闲处理器后,将 nextPollTimeoutMillis 设为 0,以便立即查找待处理的消息。

以上就是这段代码的主要逻辑。它负责不断地从消息队列中获取消息并处理,同时也处理了一些特殊情况,如消息队列已停止、空闲处理等。这种消息循环机制保证了 Android 应用程序的响应性和流畅性。

核心代码:

给定代码中最重要的部分是主循环,该循环处理消息并运行空闲处理程序,在Android消息循环系统中负责处理消息并及时有效地运行空闲处理程序。让我们逐步分解关键组成部分及其重要性:

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
for (;;) {
// ...
nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized (this) {
// ...
if (msg != null) {
if (now < msg.when) {
// ...
} else {
// 收到一条消息。
mBlocked = false;
// 从队列中移除消息并返回。
// 这是消息处理的核心操作。
// 它允许根据其预定时间依次执行消息。
// 返回的“msg”对象包含用于处理的数据和指令。
msg.markInUse();
return msg;
}
} else {
// 没有更多消息。
nextPollTimeoutMillis = -1;
}

// 处理退出消息并处理空闲处理程序。
// ...

// 运行空闲处理程序。
// 该块遍历待处理的空闲处理程序并执行它们。
// 空闲处理程序用于在消息队列为空时执行任务。
for (int i = 0; i < pendingIdleHandlerCount; i++) {
// ...
}

// 重置空闲处理程序计数并检查是否有新消息。
// ...
}
}

关键部分及其重要性如下:

  1. nativePollOnce(ptr, nextPollTimeoutMillis);:这个本地方法用于轮询来自底层系统的传入消息和事件。它等待指定的超时时间以获取新的消息。
  2. 消息处理:
    • 循环检查是否有待处理的消息,并根据其预定时间(msg.when)对其进行处理。
    • 如果一条消息准备好执行,它会从队列中移除并返回(return msg;)。
    • 这是消息循环的核心,消息按照它们被发布的顺序逐个执行。
    • markInUse() 方法表示消息正在被处理。
  3. 退出消息和空闲处理程序:
    • 循环检查消息循环是否正在退出(mQuitting)并在需要时释放资源。
    • 还会处理空闲处理程序,这些程序在没有待处理消息时执行。
    • 空闲处理程序是在消息循环处于非活动状态时执行任务的函数。

总的来说,这段代码片段代表了Android消息循环的核心机制,它对于管理异步任务、UI交互和基于事件的编程在Android应用中至关重要。这个循环的正常运行对于维持响应迅速、交互式的用户体验至关重要。

android.os.Handler#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);
}
}

这段代码是一个 Android 消息分发方法,用于处理系统消息。在 Android 中,消息分发是消息循环的一部分,用于将消息传递给相应的处理方法或回调函数。

以下是代码的主要功能和逻辑:

  1. 方法名为 dispatchMessage,接受一个非空的 Message 对象作为参数,用于处理系统消息。

  2. 首先,代码检查 msg 中是否存在一个非空的回调函数 callback。如果存在,表示该消息具有一个回调函数,将调用 handleCallback(msg) 来处理该回调函数。

  3. 如果 msg 中没有回调函数,代码会进入下一步判断。

  4. 首先,检查是否存在一个非空的成员变量 mCallback,它是一个消息处理器的回调接口。如果存在 mCallback,代码会调用 handleMessage(msg) 来处理消息。如果 mCallback.handleMessage(msg) 返回 true,表示消息已经被处理,函数会直接返回,不会继续执行后续的 handleMessage(msg)

  5. 如果 mCallback.handleMessage(msg) 返回 false,表示 mCallback 没有处理该消息,代码会调用当前类的 handleMessage(msg) 方法来处理消息。

综合起来,这段代码实现了一个消息分发的逻辑,首先检查消息是否有回调函数,然后尝试使用回调函数处理消息。如果没有回调函数,它会检查是否存在一个全局的消息处理器 mCallback,尝试让它处理消息。如果都没有成功处理,最后会调用当前类的 handleMessage(msg) 方法来处理消息。

这种消息分发机制使得消息能够在不同的处理方法和回调函数之间进行传递和处理,提高了代码的灵活性和可扩展性。

android.os.Handler#handleMessage
1
2
3
4
5
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) {
}

这段代码是一个方法声明,用于在子类中实现消息处理的逻辑。在 Android 中,消息处理是一种常见的模式,用于处理来自消息队列的不同类型的消息。

以下是代码的主要功能和逻辑:

  1. 方法名为 handleMessage,接受一个非空的 Message 对象作为参数,用于处理消息。

  2. 这是一个抽象方法,没有具体的实现,因此需要在子类中进行实现。

  3. 子类必须重写这个方法,并根据具体的业务逻辑来处理传递进来的消息。

  4. 通过在子类中实现不同的 handleMessage 方法,可以实现不同类型的消息处理,从而达到分离不同功能的目的。

在 Android 应用程序开发中,常常会使用这种消息处理机制来进行异步任务的处理、UI 更新、线程间通信等。子类可以根据不同的需求,实现不同的消息处理逻辑,使代码结构更加清晰和模块化。

使用 handler 通信机制由主线程向子线程发送消息

我们已经了解了主线程中会创建 Looper 和 handler,也明白了期运行逻辑
下面我们在子线程中创建这一套通信机制,用于主线程向子线程发送消息

创建一个 CustomLooperThread

手动创建子线程的 Looper 和关联的 Handler,以便在子线程中处理消息。

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
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

class CustomLooperThread extends Thread {
private Handler mHandler;
private Looper mLooper;

@Override
public void run() {
Looper.prepare();
mLooper = Looper.myLooper();

mHandler = new Handler(mLooper) {
@Override
public void handleMessage(Message msg) {
// 在线程中处理消息
// ...
}
};

Looper.loop();
}

public void executeTask(Runnable task) {
if (mHandler != null) {
mHandler.post(task);
}
}

public Looper getLooper() {
return mLooper;
}

public void quit() {
if (mLooper != null) {
mLooper.quit();
}
}
}

方式二: 使用 HandlerThread 使用 HandlerThread 类封装了子线程的 Looper 和关联的 Handler 创建过程。

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
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;

public class CustomLooperThread extends HandlerThread {
private Handler mHandler;

public CustomLooperThread(String name) {
super(name);
}

@Override
protected void onLooperPrepared() {
mHandler = new Handler(getLooper()) {
@Override
public void handleMessage(Message msg) {
// 在子线程中处理消息
// ...
}
};
}

public void sendMessageToThread(int what) {
if (mHandler != null) {
Message message = mHandler.obtainMessage(what);
mHandler.sendMessage(message);
}
}
}
创建一个 MyThreadManager
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
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

public class MyThreadManager {
private MyThread mThread;
private Handler mHandler;

public MyThreadManager() {
// 创建线程并启动
mThread = new MyThread();
mThread.start();

// 在线程的消息循环中创建 Handler
mHandler = new Handler(mThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
// 在线程中处理消息
// ...
}
};
}

// 发送消息到线程中
public void sendMessage(int what) {
if (mHandler != null) {
Message message = mHandler.obtainMessage(what);
mHandler.sendMessage(message);
}
}

// 执行任务在线程中
public void executeTaskInThread(Runnable task) {
if (mThread != null) {
mThread.executeTask(task);
}
}

// 关闭线程和消息循环
public void closeThread() {
if (mThread != null) {
mThread.quit();
mThread = null;
}
}
}
引出问题
方式一,自定义 LooperHandler 创建的 CustomLooperThread 中的 getLooper 有没有问题?
1
mThread.start();

是在子线程中执行的

1
2
3
4
5
6
7
8
// 在线程的消息循环中创建 Handler
mHandler = new Handler(mThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
// 在线程中处理消息
// ...
}
};thread.start()是在子线程中

是在主线程执行的
也就是说在 mThread.getLooper() 时,有可能 mThread.start() 还没执行完
所以这里有多线程并发问题

我们看一下 HandlerThread 中是怎么解决的

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
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}

boolean wasInterrupted = false;

// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
wasInterrupted = true;
}
}
}

/*
* We may need to restore the thread's interrupted flag, because it may
* have been cleared above since we eat InterruptedExceptions
*/
if (wasInterrupted) {
Thread.currentThread().interrupt();
}


Thread 中的 Handler 和 MyThreadManager 中 Handler 有什么不同
  1. MyThread 类中的 HandlerMyThread 类中的 Handler 用于处理子线程 MyThread 中的消息。它负责监听并处理从 MyThread 类的消息循环中传递过来的消息。这个 Handler 是在 MyThread 类的 run() 方法中创建并与 Looper 关联,以便在 MyThread 线程中处理消息。
  2. MyThreadManager 类中的 HandlerMyThreadManager 类中的 Handler 用于将消息发送到 MyThread 线程。它负责将来自主线程或其他地方的消息发送给 MyThread 线程的消息循环。这个 Handler 负责与 MyThread 线程进行通信,从而实现了在主线程或其他线程中触发 MyThread 线程执行特定任务。

这里我们提到了消息循环,我们下一章说 MessageQueue

,