文章目录
一. 背景
在开发中我们常会有这种需求:进入一个界面,请求一个接口,然后将接口的数据更新至UI上显示。在Android中,为了避免阻塞主线程,网络请求这种耗时操作需要放在子线程中,但在Android中,UI控件不是线程安全的,系统不允许在子线程中去更新UI。
因此现在的情况是,网络请求需要在子线程中,但是UI的更新需要放在主线程中,那么要如何在子线程获取接口数据后,通知主线更新UI呢?由此引出本文的主角 Handler。
二. 介绍
Handler主要用于线程之间的通信问题。
以背景中的问题为例子,界面中在子线程中请求网络数据,然后在子线程中更新UI。
首先在主线程中创建一个Handler,并且执行收到消息的操作
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
//收到消息后的操作
switch (msg.what) {
case MSG_UPDATE_TV:
mTv.setText(msg.obj.toString());
break;
default:
break;
}
}
};
开启一个子线程模拟网络耗时操作,获取数据后通过Handler将消息发送至主线程
new Thread(new Runnable() {
@Override
public void run() {
// 模拟网络请求
SystemClock.sleep(3000);
Message message = Message.obtain();
message.what = MSG_UPDATE_TV;
message.obj = "接口数据";
mHandler.sendMessage(message);
}
}).start();
看到这里想必有同学心中就有个疑问了,为什么通过Handler可以将数据从子线程中发送至主线程中?
三. 源码分析
要分析Handler的原理,就要介绍一下与它息息相关的几个类
类 | 职责 |
---|---|
Handler | 用于发送与处理消息 |
Message | 线程间通信消息的载体 |
MessageQueue | 消息队列 |
Looper | 用于不断的从队列中取消息,然后进行分发 |
ThreadLocal | 存储线程作用域的局部变量 |
3.1 Message
通过Handler实现线程间的数据传输,首先要依靠Message,顾名思义,也就是消息的载体。
Message message = Message.obtain();
message.what = MSG_UPDATE_TV;
message.obj = "接口数据";
Message用于携带线程间通信的一些数据
public final class Message implements Parcelable {
// 一般用于标识消息类型
public int what;
// 传递int类型数据
public int arg1;
// 传递int类型数据
public int arg2;
// 传递普通对象,
public Object obj;
// Bundle数据
Bundle data;
// 保存发送当前消息的Handler,也是处理该消息的Handler
Handler target;
...
}
关于Message对象的创建,不建议使用new直接创建Message对象,系统存在Message的消息池,可实现消息对象的复用,避免大量创建消息对象带来的开销问题。
- 消息以链表的方式组成消息池
- 消息池存在消息的最大缓存个数,最大消息个数为50个
- 每次从消息池中获取消息时,若消息池中不存在消息,则新建消息,否则取出消息链表表头
- 回收消息时,若当前消息池中消息缓存个数小于最大消息数,则将消息加入消息池中
public final class Message implements Parcelable {
//消息池以链表的形式存储
Message next;
public static final Object sPoolSync = new Object();
private static Message sPool;
//当前消息池内存在的消息数
private static int sPoolSize = 0;
//消息池中最大的消息数
private static final int MAX_POOL_SIZE = 50;
//获取消息
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();
}
// 消息回收
void recycleUnchecked() {
...
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
//回收消息时,若当前消息池内数目小于最大数,则将消息放入消息池
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
3.2 Handler
Handler的Api可以分成三类:发送消息、处理消息、移除消息
-
发送消息
发送的数据可以是Runnable或者其他的数据类型,无论发送什么数据,最终数据会被封装成Messages对象
调用enqueueMessage将消息加入到消息队列中。
-
处理消息
消息最终由发送该Message的Handler进行处理(待会会分析),消息的处理调用Handler的dispatchMessage方法,该方法中执行了处理消息的优先级,优先级从高到底为使用postXX发送的Runnable消息 > 在Handler构造方法传入的Callback消息体 > 使用sendXXMessage发送的消息
-
移除消息
通过removeXX来移除消息队列中未处理的Message,注意只能是还未进行处理的消息
public class Handler {
public Handler(@Nullable 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();
// 若创建Handler时looper不存在,则会发生崩溃,这也是在子线程中不能直接创建Hander的原因
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread "