1、背景
在开发中我们经常会使用到Handler,对Handler的使用应该是比较熟悉的,但对Handler的工作原理可能还不是很清楚,今天来带大家深入学习Handler的工作机制。一般使用Handler的场景如下:
- 子线程和UI线程进行通信,例如:子线程负责耗时操作,并通过Handler把的结果传递到主线程。
- 可以使用Handler发送一个延时操作
在分析Handler时会涉及到一些类如:ThreadLocal、 Looper、MessageQueue、 Message,所以在分析Handler的工作机制前,需要了解这些类的作用。
2、相关知识点
ThreadLocal
功能描述:用于为每个线程保存一个本地变量,解决多个线程访问临界资源时需要进行互斥访问的问题。在Handler的工作机制中,使用ThreadLocal为每个线程保存一个Looper对象。
即每个线程有一个Looper对象。Looper
功能描述:Looper是一个循环器,内部实现是一个死循环的从消息队列中取出消息并处理。每个线程对应一个Looper对象,一个线程初始情况下是没有Looper对象的。用户需在要创建Looper的线程中调用Looper.prepare( )方法,为该线程创建Looper对象。MessageQueue
上面在阐述Looper的作用时说过,Looper的内部实现是在内部通过循环不断从消息队列中取出消息并处理,而这个消息队列就是MessageQueue,每个Looper对象内部有一个消息队列MessageQueue。Message
Message就是保存在消息队列中的一个消息,Looper循环读取的就是一个个Message对象。
Handler工作流程如下:
3、分析
从上面的工作流程图中,Looper的工作比较关键,所以先从Looper的创建和启动开始分析。
一个线程初始情况下是没有Looper对象的,用户需在线程中调用Looper.prepare( )方法为该线程创建一个Looper对象。
我们先看Looper中包含哪些重要的属性
public final class Looper {
// 用于保存每个线程的Looper对象
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 消息队列,Looper内部实现就是从该队列中取出消息并处理
final MessageQueue mQueue;
// 该Looper所属的线程
final Thread mThread;
...
}
private static void prepare(boolean quitAllowed) {
// 如果当前线程已创建Looper对象,则抛出异常,一个线程只能有一个Looper对象
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 创建一个Looper对象,并保存到TheadLocal
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
// 创建了一个队列
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper对象已经创建完毕,但是这时Looper对象还没有开始工作,需手动调用Looper.loop()方法
public static void loop() {
...
// 开启一个死循环不断的从队列中取出消息
final MessageQueue queue = me.mQueue;
for (;;) {
// 如果Message是null,那么就Looper就会停止工作
Message msg = queue.next();
...
// 取出msg后,由msg中指定的target来处理该msg,target是msg指定的处理对象
msg.target.dispatchMessage(msg);
// 回收Message对象
msg.recycleUnchecked();
}
}
MessageQueue就是实现一个队列的功能,用于存储消息,内部通过一个链表的方式来实现队列的数据结构。该队列不止提供先入先出的排队功能,还可以优先使某种类型的消息先出栈。在分析Message时会详细讲解。
Message就包含了两种信息:
- 指定由谁来处理Message,在上面的分析中Message就是有target指定的对象来处理Message的
- 携带数据,这样我们就可使用Message在不同线程中进行通信和传递数据
下面来看一下Message的源码
target就是Message内部指定的用于处理该Message的处理器,类型为Handler。
public final class Message implements Parcelable {
...
Handler target;
...
}
Message中用于存储数据的属性如下,三个int类型,一个对象的引用,还有一个Bundle类型,我们可以根据需要选择使用不用的属性进行传递数据
public final class Message implements Parcelable {
...
public int what;
public int arg1;
public int arg2;
public Object obj;
Bundle data;
...
}
在Looper的内部,但取出一个Message后,直接交个Handler.dispatchMessage()来处理
// 参数msg就是待处理的消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// callback是Message指定的动作,当Message指定callback,那执行Message.callback这个动作
handleCallback(msg);
} else {
// 如果Message没有指定动作,则有Handler来处理
// 优先交给Handler指定的mCallback处理,如果mCallback返回true,说明mCallback已经处理掉,不再传递给handleMessage()
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 我最常用的就是重写handleMessage()这个方法来处理Message
handleMessage(msg);
}
}
Message的创建方式
- new Message()创建新消息
- Android提供一个Message缓存池,通过Message.obtain( )从缓冲池中取出一个Message
提示:在Android开发中使用到Message地方非常多,不能要用Message就创建一个新的,这样频繁的创建和回收对象影响性能,所以提供Message缓存池,提高Message的使用率
public final class Message implements Parcelable {
// 通过构造函数创建
public Message() {
}
...
// 缓存池的数据结构为一个链表,sPool为链表的头
private static Message sPool;
private static int sPoolSize = 0;
// 缓存池中最多可以缓存50个Message
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();
}
}
那么Message对象什么时候指定消息处理器,即Message.target属性什么时候赋值?
接着分析Handler,个人认为Handler在消息机制中扮演两种角色:
- 为开发者提供Handler.postMessageXXX( )接口,用于添加一个消息到队列MessageQueue
- Message对象指定的用于处理Message消息的处理器,这时Handler扮演的是消息处理器
在创建Handler对象时,需要为Handler对象指定一个Looper,如果不指定Looper对象,则默认使用当前线程的Looper对象,构造方法如下:
// 构造方法可以传入一个Looper对象
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(Callback callback, boolean async) {
// 使用当前线程的Looper对象
mLooper = Looper.myLooper();
// 如果不存在与当前线程相关联的Looper对象,则抛异常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 每个Handler对象,都作用于一个特定的消息队列上
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
当通过Handler.postMessageXXX( )接口发送消息时,最终都会调用enqueueMessage( )方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 指定了Message对象的处理器为当前的Handler对象
msg.target = this;
// 当设置这个值时,Looper会优先处理这个消息
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 添加到队列中
return queue.enqueueMessage(msg, uptimeMillis);
}
4、总结
Handler的工作机制已经讲解完成,来总结一下:
每个线程对应一个Looper对象,创建Looper对象时会创建一个消息队列MessageQueue,Looper的工作是循环从MessageQueue中取出Message对象,并交给Message对象指定的Handler来处理。用户可以使用Handler对象向消息队列发送消息。下面给出了上面分析类的类图。