AndroidHandler机制总结

1.What?

Handler机制是Android中的线程间通信方式。在Android中由于主线程是不安全的,因此我们所有涉及到UI的操作必须在主线程中执行。这就导致线程间频繁的通讯,比如我们常常需要从网络上获取数据再加载到UI上显示,这样一个过程就必然涉及到线程间的通讯:主线程中创建新线程->在新线程中发起网络请求->获得网络数据->后台处理后回调通知UI线程(线程间通信)并把数据发送给UI线程->UI线程中调用UI相关方法,重新加载数据到UI上。

在以上这个过程中,没有一套良好的线程间通讯方式是不行的,Android为我们提供了Handler来解决这个问题,本篇就来总结回顾Handler相关的内容。

2.How?

先来看一看Handler的使用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class LooperThread extends Thread {  
public Handler mHandler;

public void run() {
Looper.prepare();

mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};

Looper.loop();
}
}

可以看到,一个线程中持有一个Handler,在run方法中首先要初始化Looper,调用它的prepare()方法,并在下面开始Looper的循环,为什么要使用Looper、Looper和Handler有什么关系,这在接下来讲。

3.Why?

接下来我们分析Handler内部的实现机制。

在开始之前,首先让我们搞清楚Handler涉及到哪些类。

Handler类图

Handler、Looper、MessageQueue、Message、Thread,这就是Handler机制中的主要类。

Thread位于最底层,它持有一个Looper对象和一个MessageQueue对象,Looper以及MessageQueue为上层的Handler底层实现,对于一般开发,我们仅仅需要使用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
/**
* A Handler allows you to send and process {@link Message} and Runnable
* objects associated with a thread's {@link MessageQueue}. Each Handler
* instance is associated with a single thread and that thread's message
* queue. When you create a new Handler, it is bound to the thread /
* message queue of the thread that is creating it -- from that point on,
* it will deliver messages and runnables to that message queue and execute
* them as they come out of the message queue.
*
* <p>There are two main uses for a Handler: (1) to schedule messages and
* runnables to be executed as some point in the future; and (2) to enqueue
* an action to be performed on a different thread than your own.
*
* <p>Scheduling messages is accomplished with the
* {@link #post}, {@link #postAtTime(Runnable, long)},
* {@link #postDelayed}, {@link #sendEmptyMessage},
* {@link #sendMessage}, {@link #sendMessageAtTime}, and
* {@link #sendMessageDelayed} methods. The <em>post</em> versions allow
* you to enqueue Runnable objects to be called by the message queue when
* they are received; the <em>sendMessage</em> versions allow you to enqueue
* a {@link Message} object containing a bundle of data that will be
* processed by the Handler's {@link #handleMessage} method (requiring that
* you implement a subclass of Handler).
*
* <p>When posting or sending to a Handler, you can either
* allow the item to be processed as soon as the message queue is ready
* to do so, or specify a delay before it gets processed or absolute time for
* it to be processed. The latter two allow you to implement timeouts,
* ticks, and other timing-based behavior.
*
* <p>When a
* process is created for your application, its main thread is dedicated to
* running a message queue that takes care of managing the top-level
* application objects (activities, broadcast receivers, etc) and any windows
* they create. You can create your own threads, and communicate back with
* the main application thread through a Handler. This is done by calling
* the same <em>post</em> or <em>sendMessage</em> methods as before, but from
* your new thread. The given Runnable or Message will then be scheduled
* in the Handler's message queue and processed when appropriate.
*/

让我先来翻译一遍:

Handler允许你发送和处理Message与在MessageQueue中实现了Runnable接口的对象(以下简称Runnable对象)。

每一个Handler实例与一个Thread和Thread内包含的message queue相关联。

当你创建一个新的Handler实例,它会被绑定到它所在的Thread以及其message queue。从创建以后,Handler实例会把message和Runnable对象发送到message queue,并且在它们从message queue中出队后执行它们。

Handler有两个主要的功能:(1)调度message和Runnable对象使它们在未来被执行。(强调时间,如延时发送信息)(2)将一个动作送入队列,使它能够在其它线程被执行。(强调线程)

Handler的消息调度功能在post方法、postAtTime方法、postDelayed方法、sendEmptyMessage方法、sendMessage方法、sendMessageAtTime方法、sendMessageDelayed方法中完成。post开头的一类方法中调度的Runable对象会在message queue准备好后马上执行,sendMessage开头的一类方法允许你传递一个带有bundle(额外数据)的message,这个bundle在Handler的handleMessage中被回调处理,因此我们需要自己重写这个handleMessage。

当发送给Handler,你可以决定当message queue准备好就马上执行,或者是延迟一段时间再执行。后缀为Delayed的方法就是这样使用的。

当你的app中一个进程创建时,这个进程中的主线程会拥有一个专用的message queue,主线程关注管理顶层对象(activity、broadcast receiver等)和它们创建的所有windows。(也就是说主线程默认有一个message queue,另一层意思就是其它线程默认是没有message queue的,需要我们手动创建。)你可以创建自己的线程,并通过Handler和主线程交互,通过使用之前提到的post、sendMessage等方法来实现。发送给主线程的message和Runnable对象将会在合适的时候被处理。

通过这一段注释,我们对Handler的作用已经有了大致的印象。Handler把Message或者Runnable对象发送到一个线程的Message Queue中,Message可以携带数据、Runnable对象可以自己决定执行内容,我们还可以自己决定是即刻调用还是延迟调用,Message Queue中含有大量的消息,它们在合适时候就会出队列并被执行。

接下来是MessageQueue

1
2
3
4
5
6
7
8
9
/**
* Low-level class holding the list of messages to be dispatched by a
* {@link Looper}. Messages are not added directly to a MessageQueue,
* but rather through {@link Handler} objects associated with the Looper.
*
* <p>You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
public final class MessageQueue {

MessageQueue是一个持有将被Looper分发的message的list的底层类。Message不能被直接添加到MessageQueue中,而是通过与MessageQueue对应的Looper所绑定的Handler。

你可以通过Looper.myQueue方法来得到当前线程的MessageQueue。

最后是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
/**
* Class used to run a message loop for a thread. Threads by default do
* not have a message loop associated with them; to create one, call
* {@link #prepare} in the thread that is to run the loop, and then
* {@link #loop} to have it process messages until the loop is stopped.
*
* <p>Most interaction with a message loop is through the
* {@link Handler} class.
*
* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }</pre>
*/
public final class Looper {

这个类用于一个线程运行message的循环。一个线程在默认情况下没有与它们绑定,所以在要循环的线程中使用Looper.prepare方法,并使用Looper.loop方法来处理message直到循环结束。

Looper和message loop的相互作用大部分通过Handler来完成。

以上几个类的基本注释都翻译完了,看完这几类的注释,我觉得其实已经比较清楚了。

让我来简单的总结一下

Handler机制要实现的无非就是线程间的通信,那么以什么样的方式实现好呢?

很简单,每一个线程持有一个MessageQueue,在线程内使用一个Looper对象来让MessageQueue里的Message流动起来。Looper和MessageQueue本身都不能跨越线程,Handler就是一个联系不同线程中的MessageQueue和Looper的中介。Handler本身可以说是一个跨线程的对象,为什么这样说呢?因为Handler可以发送和接受处理Message,而且发送和接受所在的线程可以不同!Handler这个对象在另外一个线程使用时,发送Message到创建它的线程中的MessageQueue中,然后经由Looper对MessageQueue的处理,在合适的时机取出MessageQueue,传递给Handler处理。

一句话:多个线程使用Handler,任何一个线程都可以使用Handler发送Message到创建它的线程,这个Message也只能被创建Handler的线程处理。

Handler机制的基本原理就是这样。

下面我们来看一个简单的Demo。

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
package com.zuliangwang.learnhandler;

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Log.d("TestActivity","receive");
}
};



new TestThread().start();
new TestThread2(handler).start();
}
}

我在一个Activity(主线程)里创建了两个线程并启动它们,同时我在主线程里创建了一个Handler,因此这个Handler依附于主线程,那么我在另外的线程里发送Message,处理它的也应该是主线程,OK,下面让我们来验证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.zuliangwang.learnhandler;

import android.util.Log;

/**
* Created by zuliangwang on 16/11/22.
*/


public class TestThread extends Thread{
@Override
public void run() {
Log.d("TestThread","begin");
Log.d("TestThread","end");
}
}

这个TestThread是一个空的Thread,我创建这个Thread是为了验证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
package com.zuliangwang.learnhandler;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

/**
* Created by zuliangwang on 16/11/22.
*/


public class TestThread2 extends Thread{

public TestThread2(Handler handler) {
mHandler = handler;
}

Handler mHandler;

@Override
public void run() {
Log.d("TestThread2","id = "+getId());
Log.d("TestThread2","begin");
Looper.prepare();
Log.d("TestThread2","loop");


mHandler.post(new Runnable() {
@Override
public void run() {
Log.d("TestThread2","run"+Thread.currentThread().getId());;
}
});
mHandler.sendEmptyMessage(3);

//死循环方法
Looper.loop();
Log.d("TestThread2","end");
}
}

可以看到,我在TestThread2中使用主线程创建的Handler发送了一个Runnable对象和一个空的Message。

result输出的结果如图,我来一点点分析。

1.TestThread中输出了begin和end,证明run方法跑完了。

2.TestThread2中输出了TestThread2的ID为135,而下面run的却是1,看到了吗?两者线程ID不同!这说明什么,handler调用post方法发送的Runnable对象在另外一个线程中运行,而ID为1的线程是谁?我不说大家都应该猜得到,就是主线程!也就是说这个Handler发送的Runnable最后进入了主线程的MessageQueue并在主线程运行,这和之前说明的原理是吻合的。

3.然后再来看看TestThread输出的begin和loop,可以看到,它最后没有输出end,也就是说run方法没有正常运行结束,这是为什么呢?因为Looper!

4.最后可以看到在Activity中收到了TestThread2发送的Message。

以上和我们之前的分析完全吻合,Handler的原理到这里就搞清楚了。

下面让我们看看一直说的Looper是怎么回事,为什么TestThread2的run方法没有正常执行完毕。

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
/**
* 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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}

final long traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}

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

看到了这行吗? for (;;) Looper内的loop方法是一个死循环,这是为什么呢?想一想一个正常的线程应该是什么样的?从被创建到运行完run方法里面的代码,然后正常退出,那么我怎么样在线程中构建出UI界面这种永不结束的效果呢?为什么Android的UI可以在主线程中一直存在?很简单,因为主线程默认调用了Looper的loop方法,永不结束的从MessageQueue里取出Message处理,而我们刷新UI也就是向这个MessageQueue发送了一条Message。

到这里Handler机制就分析完了,具体的代码细节也不说了,大概的流程和逻辑现在应该很清楚了。