前言
相信不少人在面试Android工程师时,会被问到这两个问题:
- Android中的Looper、Handler、Message之间关系(Android消息处理机制)
- Android事件分发机制
本篇章就对第一种从源码角度给大家做一下讲解,有不对之处敬请谅解并指明。
概述
Handler 、 Looper 、Message 这三者是Android异步消息处理线程的重要组成部分。 异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。 简而言之,Looper负责的就是创建一个MessageQueue,然后进入一个无限循环体不断从该MessageQueue中读取消息,而消息的创建者就是一个或多个Handler 。
相关概念
相关概念的解释
- 主线程(UI线程) 定义:当程序第一次启动时,Android会同时启动一条主线程(Main Thread)
作用:主线程主要负责处理与UI相关的事件 - Message(消息) 定义:Handler接收和处理的消息对象(Bean对象)
作用:通信时相关信息的存放和传递 - ThreadLocal 定义:线程内部的数据存储类
作用:负责存储和获取本线程的Looper - Message Queue(消息队列) 定义:采用单链表的数据结构来存储消息列表
作用:用来存放通过Handler发过来的Message,按照FIFO先进先出执行 - Handler(处理者) 定义:Message的主要处理者
作用:负责发送Message到消息队列&处理Looper分派过来的Message - Looper(循环器) 定义:扮演MessageQueue和Handler之间桥梁的角色
作用:- 消息循环:循环取出Message Queue的Message
- 消息派发:将取出的Message交付给相应的Handler
图例解释关联
简要说明 详细流程图
源码分析
Looper
对于Looper主要是prepare()和loop()两个方法。 首先看prepare()方法1
2
3
4
5
6
7
8
9
10public static void prepare() {
prepare(true);
}
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));
}
sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。可以看到,在第9行,将一个Looper的实例放入了ThreadLocal,并且6-8行判断了sThreadLocal是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例~相信有些哥们一定遇到这个错误。
下面看Looper的构造方法:
1 | private Looper(boolean quitAllowed) { |
在构造方法内,新建了一个MessageQueue(消息队列),并将当前的线程信息保留。
然后我们看loop()方法:
1 | /** |
第6行,myLooper方法1
2
3
4
5
6
7/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
通过sThreadLocal将之前Looper.prepare()存入的Looper对象取出,取出后命名为“me”。如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。
第10行:拿到该looper实例中的mQueue(消息队列)
第17-59行:for (;;),进入无限循环 第18行:取出一个消息,如果没有消息则阻塞。
第36行:msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。msg的target是什么呢?其实就是handler对象,下面会进行分析。
第58行:释放消息占据的资源。
Looper的退出
当然Looper也提供了两个方法可以退出一个Looper:
- quit():quit会直接退出Looper
- quitSafety():quitSafety只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后退出Looper
这两个方法都是调用Message.quit(boolean safe)
方法。
Looper
1 |
|
MessageQueue
1 | void quit(boolean safe) { |
可见,quitSafely
时调用removeAllFutureMessagesLocked()
,quit
时调用removeAllMessagesLocked()
。
1 | private void removeAllMessagesLocked() { |
Message
1 | void recycleUnchecked() { |
从上面的代码可见,removeAllMessagesLocked()
是直接将消息队列内的所有消息直接清除回收掉;而removeAllFutureMessagesLocked()
是将时间when>now
的消息清除回收,这一般对delay的消息有影响。
SystemClock.uptimeMillis()
是自系统启动后到现在的时间(不包含系统深度水睡眠时间)。
Looper主要作用:
与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。
好了,我们的异步消息处理线程已经有了消息队列(MessageQueue),也有了在无限循环体中取出消息的哥们,现在缺的就是发送消息的对象了,于是乎:Handler登场了。
Handler
使用Handler之前,我们都是初始化一个Handler实例。所以我们首先看Handler的构造方法,看其如何与MessageQueue联系上的,它在子线程中发送的消息(一般发送消息都在非UI线程)怎么发送到MessageQueue中的。
Handler源码链接
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
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/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Handler() {
this(null, false);
}
/**
* Constructor associates this handler with the {@link Looper} for the
* current thread and takes a callback interface in which you can handle
* messages.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*
* @param callback The callback interface in which to handle messages, or null.
*/
public Handler(Callback callback) {
this(callback, false);
}
/**
* Use the provided {@link Looper} instead of the default one.
*
* @param looper The looper, must not be null.
*/
public Handler(Looper looper) {
this(looper, null, false);
}
/**
* Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages.
*
* @param looper The looper, must not be null.
* @param callback The callback interface in which to handle messages, or null.
*/
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
/**
* Use the {@link Looper} for the current thread
* and set whether the handler should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(boolean async) {
this(null, async);
}
/**
* Use the {@link Looper} for the current thread with the specified callback interface
* and set whether the handler should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
/**
* Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages. Also set whether the handler
* should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param looper The looper, must not be null.
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
没传Looper的构造方法,通过Looper.myLooper()获取当前线程保存的Looper实例(第93行),并获取这个Looper实例中保存的MessageQueue(消息队列)(第98行);
传Looper的构造方法,直接保存传入的looper对象(第123行),并获取该looper对象中保存的MessageQueue(消息队列)(第124行);
这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。
然后看我们最常用的sendMessage方法
1 | public final boolean sendMessage(Message msg) |
sendMessage -> sendMessageDelayed -> sendMessageAtTime
辗转反则最后调用了sendMessageAtTime,在此方法内部有直接获取MessageQueue然后调用了enqueueMessage方法,我们再来看看此方法:1
2
3
4
5
6
7private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
enqueueMessage方法中首先为msg.target赋值为this,【如果大家还记得Looper的loop方法会取出每个msg然后交给msg.target.dispatchMessage(msg)去处理消息】,也就是把当前的handler作为msg的target属性。最终会调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。
现在已经很清楚了Looper会调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息。然后再回调创建这个消息的handler中的dispathMessage方法,下面我们赶快去看一看这个方法:
1 | public void dispatchMessage(Message msg) { |
第2行:判断msg.callback是否为空。不为空,则调用Handler.handleCallback()方法,该方法内其实就是调用了msg.callback.run()1
2
3private static void handleCallback(Message message) {
message.callback.run();
}
第5行:判断mCallback是否为空,mCallback是Handler内的一个成员变量,该值是在Handler构造方法处传入的。若mCallback不为空,则调用mCallback.handleMessage(),若mCallback.handleMessage()返回true,则结束,否则继续掉用Handler.handleMessage()方法。 mCallback在Handler内定义为内部接口1
2
3public interface Callback {
public boolean handleMessage(Message msg);
}
而Handler.handleMessage()方法是一个空方法。因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。1
2public void handleMessage(Message msg) {
}
例如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15private Handler mHandler = new Handler()
{
public void handleMessage(android.os.Message msg)
{
switch (msg.what)
{
case value:
break;
default:
break;
}
};
};
到此,这个流程已经解释完毕,让我们首先总结一下
- 首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
- Looper.loop()会让当前线程进入一个无限循环,不断从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
- Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。
- Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
- 在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。
主线程(UI线程)处理
根据上面的例子,为什么Handler可以在主线程中直接可以使用呢?
答案是:
因为主线程(UI线程)的Looper在应用程序开启时创建好了,即在ActivityThread.main方法中创建的,该函数为Android应用程序的入口
ActivityThread源码链接
1 | public static void main(String[] args) { |
依次调用了Looper.prepareMainLooper()和Looper.loop(),完成Looper的初始化、将当前Looper记录为sMainLooper(主线程Looper)和对消息队列的轮询。1
2
3
4
5
6
7
8
9public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
Handler.post(),Handler.sendEmptyMessage()等
Handler.post()1
2
3
4
5
6
7
8
9
10
11public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
Handler.sendEmptyMessage()1
2
3
4
5
6
7
8
9
10
11public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
post()、sendEmptyMessage()等没有直接传入Message对象的,其实是对传入的参数进行包装成Message后再调用后续方法,并最终调用enqueueMessage()方法。最终和上面介绍的sendMessage()一致。
MessageQueue
前面讲了这么多关于MessageQueue(消息队列)的入和出,接下来就看看它的源码是如何的吧。
MessageQueue创建
1 | "unused") ( |
nativeInit()
为native方法,该方法内会创建一个native层的MessageQueue,并且将引用地址返回给Java层保存在mPtr变量中,通过这种方式将Java层的对象与Native层的对象关联在了一起。
存储Message,MessageQueue.enqueueMessage()
在MessageQueue中会使用enqueueMessage()方法存储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
53boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
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;
}
enqueueMessage()方法主要实现了如下功能点:
- 首先判断消息队列里有没有消息,没有的话则将当前插入的消息作为队头,并且这时消息队列如果处于等待状态的话则将其唤醒。唤醒调用native方法
nativeWake(mPtr)
- 若是在中间插入,则根据Message创建的时间(when字段)进行插入
取Message,MessageQueue.next()
1 | Message next() { |
nativePollOnce(ptr, nextPollTimeoutMillis)
是一个native方法,实际作用就是通过Native层的MessageQueue阻塞nextPollTimeoutMillis毫秒的时间。
- 如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
- 如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
- 如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
Message
之前处处提到Message,这里就简单说明一下。 Message就是用来存储各种特定信息的Bean对象,主要有如下几个成员变量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
119public final class Message implements Parcelable {
/**
* User-defined message code so that the recipient can identify
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
*/
public int what;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg1;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg2;
/**
* An arbitrary object to send to the recipient. When using
* {@link Messenger} to send the message across processes this can only
* be non-null if it contains a Parcelable of a framework class (not one
* implemented by the application). For other data transfer use
* {@link #setData}.
*
* <p>Note that Parcelable objects here are not supported prior to
* the {@link android.os.Build.VERSION_CODES#FROYO} release.
*/
public Object obj;
/*package*/ int flags;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
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();
}
/**
* Return a Message instance to the global pool.
* <p>
* You MUST NOT touch the Message after calling this function because it has
* effectively been freed. It is an error to recycle a message that is currently
* enqueued or that is in the process of being delivered to a Handler.
* </p>
*/
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
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 = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
这里主要讲一下其中的两个方法:obtain()和recycle()。
Message通过其成员变量next、sPool构成了一个单向链表形成了消息重用的消息池。
sPool记录列表头,next指向列表下一项。
obtain()方法是从该消息池(单向链表)中由头部取出一个Message,若该消息池为空(sPool==null)则直接new一个消息对象。
recycle()则是重置Message对象内的成员变量,并将其加入到单向链表(消息池)头部
面试回答
请解释下Android通信机制中Message、Handler、MessageQueue、Looper的之间的关系?
首先,是这个MessageQueue,MessageQueue是一个消息队列,它可以存储Handler发送过来的消息,其内部提供了进队和出队的方法来管理这个消息队列,其出队和进队的原理是采用单链表的数据结构进行插入和删除的,即enqueueMessage()方法和next()方法。这里提到的Message,其实就是一个Bean对象,里面的属性用来记录Message的各种信息。
然后,是这个Looper,Looper是一个循环器,它可以循环的取出MessageQueue中的Message,其内部提供了Looper的初始化和循环出去Message的方法,即prepare()方法和loop()方法。在prepare()方法中,Looper会关联一个MessageQueue,而且将Looper存进一个ThreadLocal中,在loop()方法中,通过ThreadLocal取出Looper,使用MessageQueue.next()方法取出Message后,判断Message是否为空,如果是则Looper阻塞,如果不是,则通过dispatchMessage()方法分发该Message到Handler中,而Handler执行handlerMessage()方法,由于handlerMessage()方法是个空方法,这也是为什么需要在Handler中重写handlerMessage()方法的原因。这里要注意的是Looper只能在一个线程中只能存在一个。这里提到的ThreadLocal,其实就是一个对象,用来在不同线程中存放对应线程的Looper。
最后,是这个Handler,Handler是Looper和MessageQueue的桥梁,Handler内部提供了发送Message的一系列方法,最终会通过MessageQueue.enqueueMessage()方法将Message存进MessageQueue中。我们平时可以直接在主线程中使用Handler,那是因为在应用程序启动时,在入口的main方法中已经默认为我们创建好了Looper。