Android 中 Handler,Looper,HandlerThread 的使用

Android 中非UI 线程(WorkThread)不能操作UI 线程(MainThread),那线程之间通信我们该怎么办?在Java 我们可以通过多线程来处理,但是Java 的多线程晦涩难懂,所以Android 的引入了Handler,Looper 的机制来处理线程间的通信。

其实网上有很多人都写过Handler,Looper 的教程,但是看别人的东西就像看过猪跑,和吃过猪肉的感觉完全不一样(这个恰当可能不太恰当,各位还是不要对号入座),所以我就自己来重新梳理梳理以便于自己理解更加深刻。

先来看看他们的工作流程吧
图片来自http://anany.me/2015/04/12/handler/

handler 发送Message 给MessageQueue,Looper 来轮询消息,如果有Message,然后再发送给Handler,Handler 拿到消息就可以所在的线程执行了。

一般就两种情况,MainThread 发送给WorkThread 和WorkThread 发送给MainThread,特别常用的情景就是我们在WorkThread 执行一些耗时的操作(网络请求,文件读写),返回的数据来更新MainThread,下面写个例子看一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

public static final int WHAT_ONE = 1;

Handler mMainHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case WHAT_ONE:
Toast.makeText(HandlerActivity.this, "what one", Toast.LENGTH_SHORT).show();
break;
}
super.handleMessage(msg);

}
};

private void sendToMain() {
runOnUiThread(new Runnable() {
@Override
public void run() {
mMainHandler.sendEmptyMessage(WHAT_ONE);
}
});
}

在主线程创建一个Handler 重写handlerMessage(Message msg) 方法,然后使用
handler 发送消息给Handler,在handlerMessage() 来处理,如果发送很多消息它是怎么区别的,其实Message 对象中有个what 字段,比这这个例子sendEmptyMessage() 就是发送一个空消息,只需要传一个what 字段,以便于Handler 接收消息来区分,发送消息这一块下面会详细讲。


另一种就是WorkThread 发送给MainThread

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
private LooperThread looperThread;

class LooperThread extends Thread {
public Handler mWorkHandler;

@Override
public void run() {
super.run();
Looper.prepare();
mWorkHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case WHAT_ONE:
mMainHandler.sendEmptyMessage(msg.what);
break;
}
super.handleMessage(msg);
}
};
Looper.loop();
}
}

private void sendToWork() {
looperThread = new LooperThread();
looperThread.start();
}

private void sendToWork2() {
if (looperThread.mWorkHandler != null) {
looperThread.mWorkHandler.sendEmptyMessage(WHAT_ONE);
}
}

这个和上面的感觉有点不同,我们会创建一个线程,在run() 中创建Handler,然而发现会多调用了两个方法Looper.prepare() 和Looper.loop() 这两个方法是干什么用的。但发现上面第一个并没有调用这两个方法,其实并不是没有调用,只是在它的父类中ActivityThread 的main() 方法已经调用了。接下来就看看这两个方法到底干了什么。

Looper.prepare()

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

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

// 实例化Looper
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

会发现将实例化一个Looper 传给给sThreadLocal,只有sThreadLocal 为空的时候,才会传一个Looper那么就说sThreadLocal 只有一个Looper,如果在WorkThread 多次调用或者不调用都会报Only one Looper may be created per thread
而sThreadLocal 在全局已经初始化了,ThreadLocal 允许我们创建只能一个当前线程属于的对象当前读写的变量,就算其他线程中调用,也执行get 和set 方法,也无法获取这个的值,因此ThreadLocal 和Looper 是一一对应的。

Looper 实例化会创建一个MessageQueue 对象,它主要就是消息队列,来存储消息的。

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

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;

for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// 最后会调用handlermMessage()
msg.target.dispatchMessage(msg);

msg.recycleUnchecked();
}
}



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

看上面的loop() 的代码,我把一些不管紧要的代码都去掉了,核心代码基本上就这些了,首先我们会调用myLooper() 的方法,获取Looper 对象,然后再获取MessagQueue,然后就开始轮训了,获取Message,target 字段就是Handler 对象,因为每次发送消息都会讲当前的Handler 传送过去,dispatchMessage 最后调用handleMessage()。然后我们就在handleMessage() 根据what 字段来识别不同地方发送消息,然后做出相应的动作。

更深入的可以阅读下面的参考。

参考


http://anany.me/2015/04/12/handler/#
http://developer.android.com/intl/zh-cn/reference/android/os/Handler.html
http://blog.dreamtobe.cn/2016/03/11/android_handler_looper
https://hit-alibaba.github.io/interview/Android/basic/Android-handler-thread-looper.html