Android Handler 机制详解

一些基础回顾

引子

主线程:也叫UI线程,或称ActivityThread,用于运行四大组件和处理他们用户的交互。 ActivityThread管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),在Android系统中,在默认情况下,一个应用程序内的各个组件(如Activity、BroadcastReceiver、Service)都会在同一个进程(Process)里执行,且由此进程的主线程负责执行。
ActivityThread既要处理Activity组件的UI事件,又要处理Service后台服务工作,通常会忙不过来。为了解决此问题,主线程可以创建多个子线程来处理后台服务工作,而本身专心处理UI画面的事件。

子线程: 用于执行耗时操作,比如 I/O操作和网络请求等。(安卓3.0以后要求耗访问网络必须在子线程种执行)更新UI的工作必须交给主线程,子线程在安卓里是不允许更新UI的。

简而言之,就是说:

  • 主线程不能执行耗时操作(避免ANR)
  • 子线程不能直接更新UI界面

Handler 的简单使用

1
2
3
4
5
6
7
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO: implement this method.
}
};

在新的线程中:

1
2
3
Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);

简单的Handler使用
简单的Handler使用

上述代码和图片介绍了handler最简单的一种使用方法和过程,下面我们按过程详细介绍handler机制。

Handler 机制的分析理解

下面,我们按照消息从创建、发送到处理的过程,结合源代码理解Handler机制。

Message简介

第一步当然是创建一个消息。在创建新Message时,我们使用Message.obtain()方法来新建一条消息,而不是直接new Message()。我们不妨来看一下obtain方法的源代码

Message的源代码链接:android.os.Message源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 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;
sPoolSize--;
return m;
}
}
return new Message();
}

可见从obtain()的源代码中我们可以知道,它是静态方法,而且只有在spool == null的情况下才会new出一个Message(),返回一个Message对象,如果在不为空的情况下,Message的对象都是从Message对象池里面拿的实例从而重复使用的,这也为了Android中的Message对象能够更好的回收。

说到回收,我们看看跟回收有关的方法。最后追溯到recycleUnchecked()方法。

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
/**
* 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++;
}
}
}

我们继续深入Message类来看。首先看一下其中几个重要的成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int what;
public int arg1;
public int arg2;
public Object obj;
Bundle data;
Handler target;
Runnable callback;
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;

其中,前五个不再赘述。其中what用于识别(identify)消息。后面四个都是msg对象中携带的数据(data)。

target指的是发送这条消息的Handler,在发送消息时,这个msg对象会持有一个对Handler的引用。这个后面分析Handler源码时细说。
callback是实现了Runnable接口,当这个msg对象的callback不为null时,在分发Message时会回调这个callback,否则执行handleMessage()方法。这个后面分析时会再次细说。

sPool: 消息回收池,是个链表结构。

返回去再看看obtain和recycle方法,是不是清晰多了 : )

小结:

  • 使用message.obtain()方法新建message对象,message是重复使用的,有对象池进行回收。
  • 每个message都对应一个发送该消息的Handler。(后面详解)
  • message在被处理时会先回调message对象持有的callback,在callback为null的时候才会调用handler的handleMessage方法。(后面详解)

MessageQueue 简介

MessageQueue源码

MessageQueue中文翻译就是消息队列,它内部存储了一组信息,存放的是Message,以队列的形式对外提供了插入和删除的工作(虽然名字叫做队列,但是其内部的 存储结构是单链表)

  • 插入(入队) enqueueMessage(Message msg, long when)
  • 读取(出队) next()

顾名思义。这一部分源代码分析先跳过,随后分析。

Handler 简介

在消息创建完毕后,我们需要使用Handler的sendMessage方法发送消息。

首先当然需要new一个Handler,我们来看看Handler的构造函数和注释。

1
2
3
4
5
6
7
8
9
10
/**
* 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);
}

无参的构造函数最终追溯到如下两个构造函数

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
/**
* 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;
}

可见,在新建一个Handler时有两种情况。

  1. 如果没有指定looper,则构造函数会调用Looper.myLooper()方法来获得当前线程(i.e. 创建当前Handler的线程)的looper,并持有其引用。如果当前线程还没有初始化looper则会抛RuntimeException。
  2. 如果指定了looper,那么Handler就持有你指定looper的引用。

总而言之,每个Handler都对应有一个Looper。关于Looper的内容下面开始。

Looper 简介

Looper源代码

我们先截取部分成员变量

1
2
3
4
5
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;

ThreadLocal是什么

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

下面是looper的prepare方法和其构造函数

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
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public 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));
}
/**
* 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()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 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();
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

也就是说,根据ThreadLocal的机理,每个线程都对应一个looper。

我们通过prepare方法创建当前线程对应的looper,也就是调用了私有的构造方法。我们注意到,当looper创建的时候会随之创建一个MessageQueue,一个Looper对应一个MessageQueue。

主线程是个例外,其looper为专门的一个成员变量 sMainLooper。主线程的Looper在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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public final class ActivityThread {
(前面代码省略..............)
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// 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();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
// 创建ActivityThread实例
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}

结合上面分析的Looper可以得到如下结论:

  • 每个线程能且只能创建一个对应的looper,主线程的looper比较特殊。
  • 在looper创建时会对应生成当前线程的MessageQueue。

也就是说,线程、looper和MessageQueue某种程度上是一一对应的关系。简单来说,我们在一条线程中执行Looper.prepare()方法时,创建了属于这条线程的Looper和MessageQueue.

  • 一般情况我们使用无参的构造函数,实际上是把Handler和当前线程的Looper和MessageQueue“绑定”。也就是说在当前线程处理消息。
  • 主线程是个例外,任何一个线程中的Handler都可以绑定主线程的Looper,也就是说,无论哪个线程的Handler都可以往主线程发消息。

因为Handler、Looper和Message的三角关系被我强行拆成三个部分很难理解清楚。我先把上述的结论下了。我们再回到Handler的sendMessage()方法,看看一条消息是怎么被发送的。

回到Handler和Looper

sendMessage有一篮子方法,我们发现,关于send和post的方法里面,调来调去就是几个方法

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
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean postAtFrontOfQueue(Runnable r) {
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public 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);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

殊途同归,最后10 个方法都进入了enqueueMessage方法。

1
2
3
4
5
6
7
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

之前说过,每个message持有一个handler的引用,这个handler就是发送消息的handler,最后也会发回这个handler去处理消息。而发送消息的过程,就是在当前线程的MessageQueue里面enqueue一条消息。

那么,当消息被enqueue到队列之后,Looper是怎么样轮询消息的呢?

Looper有个重要的方法是loop,之前只是简单介绍了looper,但并没有把方法贴出来。现在我们来看看Looper.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
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
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();
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(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();
}
}

之前说过,每个message持有一个发送这个消息的handler的引用(target)。

在处理消息时,直接调用了msg.target.dispatchMessage()方法,把消息送回Handler进行处理。

结合之前的内容,再总结一下:

  • Handler发送消息仅仅是调用MessageQueue的enqueueMessage向插入一条信息到MessageQueue

  • Looper不断轮询调用MessageQueue的next方法获得下一条待处理的消息

  • 如果发现message就调用handler的dispatchMessage,ldispatchMessage被成功调用,接着调用handlerMessage()

发送消息的过程已经非常明确了。下面我们来看处理消息的过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

message对象中有一个callback成员变量,如果这个message有callback那么就按照callback的内容去处理。否则就调用正常的handleMessage方法。

题外话

除了发送消息之外,我们还有以下几种方法可以在子线程中进行UI操作:

  1. Handler的post()方法
  2. View的post()方法
  3. Activity的runOnUiThread()方法

post方法和sendMessage方法使用类似,只是post的参数是一个Runnable。我们来看看源代码

1
2
3
4
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}

很明显,是通过getPostMessage把runnable包装成了一个Message。然后再去发一条message。我们看看这个包装消息的方法

1
2
3
4
5
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

在这个方法中将消息的callback字段的值指定为传入的Runnable对象。咦?这个callback字段看起来有些眼熟啊,喔!在Handler的dispatchMessage()方法中原来有做一个检查,如果Message的callback等于null才会去调用handleMessage()方法,否则就调用handleCallback()方法。

那我们快来看下handleCallback()方法中的代码吧:

1
2
3
private final void handleCallback(Message message) {
message.callback.run();
}

竟然就是直接调用了一开始传入的Runnable对象的run()方法。

然后再来看一下View中的post()方法,post方法也是传入一个runnable。源码如下所示:

1
2
3
4
5
6
7
8
9
10
public boolean post(Runnable action) {
Handler handler;
if (mAttachInfo != null) {
handler = mAttachInfo.mHandler;
} else {
ViewRoot.getRunQueue().post(action);
return true;
}
return handler.post(action);
}

原来就是调用了Handler中的post()方法,我相信已经没有什么必要再做解释了。

最后再来看一下Activity中的runOnUiThread()方法,代码如下所示:

1
2
3
4
5
6
7
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}

如果当前的线程不等于UI线程(主线程),就去调用Handler的post()方法,否则就直接调用Runnable对象的run()方法。还有什么会比这更清晰明了的吗?

通过以上所有源码的分析,我们已经发现了,不管是使用哪种方法在子线程中更新UI,其实背后的原理都是相同的,必须都要借助Handler机制来实现。

总结

Handler机制简单概括如下:

  1. 每个线程可以初始化一个looper和MessageQueue,主线程在ActivityThread运行时已经有了。
  2. Handler负责发送消息对象,发送时会把消息对象里面的target指向自己,表明“消息是我发的”。
  3. Handler发送消息的过程就是往MessageQueue里面入队的过程。入的是哪个队取决于你的Handler,如果new的时候构造函数为空,那就是当前线程的MessageQueue。之前说了主线程的looper可以在任何地方获得(单独的成员变量),所以任何线程的Handler都可以往主线程的MessageQueue中发消息。
  4. Looper负责轮询消息,每拿到一个消息会根据它的target丢给发消息的那个Handler,调用其dispatchMessage方法去处理。

下图盗网上的,自己画不好。顺便求一波Windows和Linux上画这种流程图的工具。

先写那么多,有问题以后再改……

Handler机制过程图
Handler机制过程图