源码分析之Handler

Handler是Android中的消息机制实现,可以实现UI线程和子线程的消息传递,这里就来深入了解Android的消息机制,来分析Handler的源代码

入手实例

在Android开发中,子线程与主线程通信是再寻常不过的事情了,那么为何需要子线程和主线程通信呢,相信只要是做过Android开发都知道这是为啥,为了博客的尽可能详尽,这里还是说说原因

举个简单例子,以前刚做Android开发的时候,啥也不懂,记得有一次,在主线程中做了耗时操作,具体记不得啥操作了,然后一运行,报了个无响应,也就是常说的ANR,然后就知道了主线程阻塞,耗时的操作放在子线程,主线程一旦阻塞超过5s就会报ANR,既然主线程不做耗时操作,那么子线程里面执行怎么和主线程互动呢,由此便引生出了Handler,当然AsyncTask也是基于同样的道理,AsyncTask的源代码在之前已经分析过了,这里就不再赘述

那么在开发中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
public class MainActivity extends Activity {

private static final String TAG = "cj5785";
private TextView textView;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d(TAG, "handleMessage: " + Thread.currentThread().getName());
String str = (String) msg.obj;
textView.setText(str);
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text_view);
}

public void buttonClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
Log.d(TAG, "run: " + Thread.currentThread().getName());
Message message = Message.obtain();
message.obj = "更新UI";
handler.sendMessage(message);
}
}).start();
}
}

这是一个简单的更新UI的例子,通过log可以清楚的看到,两个打印语句是处在不同的线程中的,然而却实现了主线程和子线程的消息传递

1
2
D/cj5785: run: Thread-13741
D/cj5785: handleMessage: main

如果直接在子线程中更新UI则会报出以下错误

1
Only the original thread that created a view hierarchy can touch its views.

那么这样的线程通信是怎么实现的呢,下面就来分析一下源代码

源代码分析

分析源代码,尤其是涉及到几个类的这种代码,找准切入点很关键,那么这个分析的切入点在哪呢,子线程发出消息,主线程接收,那么很明显是子线程的发出点,那么就应该先看子线程里面代码的处理

1
2
3
Message message = Message.obtain();
message.obj = "更新UI";
handler.sendMessage(message);

Message部分

首先是生成了一个Message对象,这里有两种方式,一种是直接new一个出来,一种是使用obtain(),那就去看看这个对象是怎么生成的

1
2
3
4
5
6
7
8
9
10
11
12
13
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();
}

这里主要关注的是next参数,那么next是啥呢,有C语言基础的一看就知道是啥,没有也没关系,我们继续往下看

1
2
3
4
5
6
// 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;

相信这句注释已经很明显了,这不就是链表么,Java中的链表使用对象引用实现,那么也就是说这里判断有没有Message有的话就实现复用,没有的话就new一个,而sPoolSync就是一个同步锁而已,到这里,并没有给出主线程和子线程消息传递的理由,那么继续往下看,message.obj = "更新UI";这里只是设置了变量,也没有参考价值,那么只剩handler.sendMessage(message);了,到此貌似Message的任务就完成了,那么来看看Message里面的一些值得注意的地方吧
照例先从类开始

1
2
3
public final class Message implements Parcelable {
···
}

Message实现了Parcelable接口,Parcelable和Serializable都是实现序列化,用于消息的传输,Message是一个final类,简而言之无法继承
再看看Message的成员变量,常用那几个就不说了,这里谈一谈重要的一些

1
2
3
4
5
6
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;

private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;

首先是Bundle,这也就是Message为何可以设置Bundle的原因所在了,这里出现了Handler,也就是说在这里涉及到了Handler,这里简单来说就是Message会持有Handler的引用,这里注意到似乎只用带参的obtain()以及setTarget()才会初始化Handler,MAX_POOL_SIZE说明了Message的大小,gCheckRecycle则是清除信息的标志位

Handler部分

那么到这里已经没啥线索了,可以猜测在Handler中对target进行了初始化,那看看sendMessage()做了啥吧

1
2
3
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}

这里意外发现了常用的延迟设置,再继续看

1
2
3
4
5
6
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

所以从这就可以看出,当延迟小于零时会将其设为0,这里注意一下,SystemClock.uptimeMillis()是一个native方法,返回的是boot时间,再继续追寻

1
2
3
4
5
6
7
8
9
10
public boolean sendMessageAtTime(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);
}

到这里,出现了一个新东西,这便是Handler组成的重要部分,那么MessageQueue是啥呢,熟悉数据结构的一眼就看出来了,这其实是个队列,稍后再看这个队列做了啥,队列不为空就要调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);
}

这里,对target赋值了,然后设置了异步状态标识,然后回到了MessageQueueenqueueMessage()方法,至此,有必要暂停Handler的分析,去看一看MessageQueue

MessageQueue部分

MessageQueue中找到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
boolean enqueueMessage(Message msg, long when) {
//target不能为空
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//msg不能正在被使用
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(); //清除Message
return false;
}

msg.markInUse(); //标记正在使用
msg.when = when; //得到延迟
Message p = mMessages; //赋值,在第一次调用此方法前,mMessages为null
boolean needWake;
if (p == null || when == 0 || when < p.when) { //由于是短路或,不会执行到第三个条件
// New head, wake up the event queue if blocked.
msg.next = p; //将msg的next指向p
mMessages = msg; //将msg赋值给mMessages
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 = p.next; //p的后节点赋值给p
if (p == null || when < p.when) { //循环终止条件(到达尾节点或者when值小于后面的节点)
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; //msg的下一节点指向p
prev.next = msg; //prev的下一节点指向msg
}

// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

如果只是一个msg,那么就只形成一个节点,如果多个msg,则会按时间顺序形成一条链表,虽然形成了链表,那么我们最关心的主线程与子线程的通信去哪了,或者说我们的分析过程问题出在哪里,注意到前面的mQueue,这个东西是哪里来的

1
final MessageQueue mQueue;

那么是在那里初始化的,注意到在MainActivity中,调用前生成了Handler对象,使用的是无参的构造方法,那么来看看这个方法

1
2
3
public Handler() {
this(null, false);
}

额,好像没啥用,继续

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
}

关于第一个if判断,这里说明一下:FIND_POTENTIAL_LEAKS标志设置为true以检测扩展的Handler类,如果不是静态的匿名,本地或成员类, 这类可能会产生泄漏。也就是我们常见构造时的警告说明!至于消除警告方法一般是设置成静态或弱引用
接下来发现有个mLooper,这是一个循环器,MessageQueue和Handler之间桥梁,那么这里就跳转到Looper吧

Looper

首先看看Looper.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 @Nullable Looper myLooper() {
return sThreadLocal.get();
}

结合注释知道了,这里返回带有当前线程的Looper,下面是get啥呢,继续来看get()

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
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}

/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}

结合注释可知,这里返回的是线程值,注意sThreadLocal的生成

1
2
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

嗯,也就是说在get前要调用prepare(),从而可以推导出在ActivityThread里面调用了这个方法,查看一下

1
2
3
4
5
6
7
public static void main(String[] args) {
···
Looper.prepareMainLooper();
···
Looper.loop();
···
}

看来没错,那么再看看Looper.prepareMainLooper()

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

这里还需要注意一点,这里的set,使用的是当前线程,也就保证了一个Thread只对应一个Looper
好像还差一点,再来,感觉到曙光了

1
2
3
4
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

到这里,终于找到了关键点,也就是说主线程和子线程通过MeaasgeQueue建立起了连接,这个Queue是同一个对象,那么具体是怎么执行的,前面执行了Looper.prepareMainLooper();,后面还有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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
* 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.");
}
//得到当前线程Queue
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 (;;) { //条件循环
//执行queue的next()方法,不断从queue中取出Message
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.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//之前设置了target,这里调用Handler的dispatchMessage()方法
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();
}
}

这里就是不断循环得到Message,然后分发消息,这里的重点在于dispatchMessage()做了啥

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

可以看到,这里由于前面没有设置callback,所以执行的是handleMessage(),那么再继续看看

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

这里并没有实现这个方法,注明了要实现这个方法才能收到消息,到此,在子线程发出的消息在主线程就能够得到了
那么到这里,消息机制的源代码分析也就完成了

思路整理

这里画个图就简单明了

其工作原理就是通过Handler将消息发送给MessageQueue,而MessageQueue又是由Looper创建的,这里Handler和Looper使用的是同一个Queue,此时在Looper中通过loop()会不断从MessageQueue中读取消息,当msg全部都发送以后,回收消息。
Message: 消息对象
MessageQueen: 存储消息对象的队列
Looper: 读取MessageQueue里面的消息,交由Handler处理
Handler:发送消息和处理消息
handler一定要记得在页面退出时remove

至此,Handler消息机制就分析完毕了

Donate comment here