Handler消息机制

前言

虽然知道凡事寻求真理才是学习的最好方式。但自己总是缺乏耐心去阅读源码,甚至总觉得学习的最快方式是阅读别人的博客记住就可以了。

直至每次面试被问及原理时,才追悔莫及(当初为什么不看下源码?)……

作为一名3-4年开发经验的Android码农,竟然对Handler源码没读过,还有脸提你的工作年限?

近期的各种事,弄的心情也不太好。所以决定工作之余,静心看些自己需要充电的知识。从今天起开始学会养成阅读源码的习惯,日后的学习中也多尝试通过源码来学习了解原理。


Handler消息机制
  • Looper:轮询器。

  • Handler:消息处理器

  • MessageQueue:消息队列

  • Message:Message是实现了Parcelable的一个实体类,用于封装消息进行传递。

我们知道ActivityThread的main()方法它是我们应用启动的一个入口,在main()方法中有这样一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) {
....
//① 初始化Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));
}
...
//② 执行轮训
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}

Looper.java类

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
public final class Looper {
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建一个新的looper对象存储到ThreadLocal中。保证安全唯一。
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
* ①-----------------------------------------------------------------------------------
* 在当前线程中实例化一个Looper对象,使它作为应用的一个主Looper。
* 该looper对象由Android系统来创建为应用,所以开发者不需要调用该方法。
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
* ②-----------------------------------------------------------------------------------
*/
public static void loop() {
// 获取当前线程的looper对象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 拿到looper所维护的消息队列
final MessageQueue queue = me.mQueue;
...
for (;;) {
// 从MessageQueue中轮询消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
//③ 如果有消息则回调,target是什么?
msg.target.dispatchMessage(msg);
...
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
}
}
}

Message.java

1
2
3
4
5
6
7
8
9
10
11
12
13
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
Message next;
Runnable callback;
Bundle data;
//③ Message中的target原来是Handler对象
Handler target;
...
}

走到这里基本流程都看明白了,只剩下最后一步,那就是Message中的target是在什么时候赋值的?

这个问题我们首先想到消息Message在什么时候和Handler有过接触?

对,那就是在子线程进行handler.sendMessage(msg)的时候。我们再看Handler发送消息的源码:

Handler.java

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
public class Handler{
/**
* 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.
*
* 客官看最后一句说明:
* 你将会在该handler所属的线程中的handleMessage()方法中接收到该Message
*
* 所有sendMessageXXX()方法最终会走到该方法中执行
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 该消息队列是在Handler构造方法执行时,从当前线程的Looper中获取到的
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 嘿,抓住你了~
// ③ 在消息入队之前handler将自己的实例赋值给了要入队的Message.target
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 真正的消息入队
return queue.enqueueMessage(msg, uptimeMillis);
}
/* 所以在Looper中轮询到消息后,直接使用msg.target.dispatchMessage(msg)
* 即可将Message传递到我们在Activity中创建的Handler的回调handleMessage(msg)方法中
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
}

小结

综上,通过源码我们了解到了整个流程:

handler

  1. 应用在创建时候,由ActivityThread的main方法启动进行初始化工作,在该方法中调用了Looper.prepareMainLooper();在主线程中创建了一个Looper对象,并调用Looper.loop()方法在主线程对Looper中所维护的MessageQueue进行轮询。

  2. 当我们在Activity中创建Handler后,并在子线程中调用handler.sendMessageXXX(msg)方法进行消息发送时,Handler内部在对该Message入队之前,将自己赋值绑定给了msg.target。

  3. 当主线程中的Looper轮询到了Message。则会调用msg.target.dispatchMessage(msg)将消息通过主线程中的handler实例回传到创建Handler时候的Callback的handleMessage(msg)方法中。

如果帮到了你,想打赏支持,喏~