前言
Android消息机制分析
Handler源码分析
正文
一. 概述
Handler算是我们平时开发中比较常用的一个, Handler所代表的是Android中重要的一部分, 即消息机制; 本文将分析Handler的运行机制
1.1 使用场景
-
线程切换: 这个是使用
Handler最常见和最频繁的场景了; 我们都知道,UI线程中不允许执行耗时操作, 那么当我们需要先去获取数据(耗时)再进行UI展示或者交互的时候, 线程切换就必不可少了 -
任务延时: 即在未来某个时刻再执行任务, 这个主要是通过
Handler.postDelayed()和Handler.sendMessageDelayed()完成的(实际上postDelayed()也是通过sendMessageDelayed()完成的)
1.2 为什么会有Handler
我们都知道Handler的作用主要是用于切换线程, 那么为什么会出现Handler喃; 我们以UI线程中执行耗时操作为例, 对于耗时操作, 特别是在需要实时交互的应用中, 肯定是需要设计为多线程的, 那么Android设计之初为什么不使用多线程去访问UI呢, 最致命的一点就是, 多线程存在并发访问的不确定性, 这就会造成UI表现的不可预期性, 造成用户体验极差; 当然, 对于多线程并发的问题还是有很多方式去避免和解决的, 最常见的一个就是加锁, 那么为什么不采用多线程访问,
对UI加锁的方式实现呢, 一个是加锁会使UI逻辑变得复杂, 另一个是加锁会降低效率, 阻塞某些线程的执行
所以, 基于很多方面的考虑, 特别是对于Android应用这样交互性很强的场景而言, 使得需要去实现自己的一套异步通信机制, 需要达到的目标是, 线程之间的独立性保持和数据交互的便利性, 这也是Handler实现的
1.3 概述
在Android消息机制中, 有非常重要的几个部分, 也是我们分析的切入点:
-
MessageQueue: 单链表, 实现消息存储; 是
Java层和Native层的连接纽带 -
Looper: 无限循环查询是否有新消息
-
Handler: 消息接受和分发处理的中转站
以下是我们使用Handler最常见的一种写法, 我们通常会重写handleMessage()来实现自己的逻辑; 当在其他线程中通过Handler.sendMessage()来发送消息时, 我们就会在handleMessage()中接收到该消息
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// do somthing with msg
super.handleMessage(msg);
}
};
new Thread() {
@Override
public void run() {
mHandler.sendEmptyMessage(0);
}
}.start();
Handler进行线程切换和消息传递的一个大致流程是, 当我们通过sendMessage()发送消息的时候, MessageQueue负责存储该消息, Looper会无限循环的去检查MessageQueue中是否有消息, 如果有则调用Handler的dispatchMessage()进行消息分发, 最终会回到handleMessage中进行处理
在开始讲解之前, 需要先明确几个结论(在稍后会验证): 每个线程都可以有自己的Looper, 但是默认没有创建(除了UI线程外); Looper是创建Handler所必须的(否则会抛异常); MessageQueue和Looper是一一对应的关系, 也就是说每个线程会有自己的MessageQueue和Looper; Handler在哪个线程中创建就在哪个线程中处理消息, 其他线程发送的消息存储在对应Handler所对应的MessageQueue中
二. 构造函数
Handler提供了七个构造函数, 如下表
| Handler() | Handler(Callback callback) | Handler(Looper looper) |
| Handler(Looper looper, Callback callback) | Handler(boolean async) | Handler(Callback callback, boolean async) |
| Handler(Looper looper, Callback callback, boolean async) |
这七个构造函数最常用的是默认构造函数(即不带参数那个), 其他几个, 一类是带Callback的, 是提供的消息处理函数, 我们使用Handler一般都会重写其handleMessage()方法来处理消息, 但是也可以不重写handleMessage(), 而是在构造Handler的时候传入一个Callback回调, Callback是Handler中的一个接口, 如下; 另一类是带标志位async的, 是设置消息是否为异步处理, Handler中的消息默认是同步处理的
public interface Callback {
public boolean handleMessage(Message msg);
}
这里我们以构造函数Handler(Callback callback, boolean async)进行分析, 如下; 可以看出, 这里的mLooper是通过Looper.myLooper()获取的, 如果我们获取为null也就是说当前线程没有Looper的话, 会抛出异常, 这个异常也是我们大多数初学者会遇到的; 这里也验证了上面所说的, Looper是Handler构造所必须的, 如果当前线程没有Looper的话, 会抛异常(至于为什么说是当前线程, 接下来马上分析~)
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
那么Looper.myLooper()又是如何获取当前线程的Looper的呢; 如下, 可以看出是通过sThreadLocal获取的, sThreadLocal是一个ThreadLocal类型的实例
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
ThreadLocal不是一个线程, 而是用于在每个线程中存储数据的, ThreadLocal可以在不同的线程中互不干扰的存储并提供数据; 那么它是如何实现在不同线程中互不干扰的存取数据的呢, 我们先来看其set()方法, 可以看出, 其实际上会先去检查当前线程是否持有ThreadLocalMap, 如果有的话, 直接将value存储进去即可, 否则, 初始化当前线程的ThreadLocalMap然后再存放值, 这样就可以根据不同的线程来存取各自线程的值了
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
那么我们要构造Handler就必须获取当前线程的Looper, 那么我们又是什么时候构造和存储的Looper喃; 前面我们说了, 每个线程都可以有自己的Looper, 但是除了UI线程以外都默认没有创建; 我们先来看UI线程中是什么时候创建的Looper, UI线程就是ActivityThread, 在其main()函数中, 我们发现了如下与Looper相关的语句, 这实际上是开启了主线程的消息处理(因为要处理各种事件, 所以一开始就开启了)
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
}
我们继续看Looper.prepareMainLooper(), 其中实际上是调用了prepare(), 如下; 这里我们看到了熟悉的ThreadLocal.set(), 而且存储的是一个新的Looper, 那么这里就将线程和各自的Looper联系起来了, 我们在Handler构造函数中获取的也就是和各个线程相关的Looper了
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));
}
到这里我们知道了, perpare实际上是在准备Looper, 那么Looper.loop()又是在做啥呢, 其实Looper.loop()是开启消息处理循环, 前面我们说了Looper不断的去检测MessageQueue中是否有消息, 实际上就是在loop()中开启的
这里我们暂时不讲loop(), 而是放到后面讲消息处理的时候讲; 我们平时使用Handler的时候, 大多是在UI
线程中创建的, 因为上面我们已经看到了, UI线程中默认是创建了Looper的, 所以不需要我们再去手动创建, 但是我们如何在子线程中构造和使用Handler呢; 其实在Looper的文档中已经给出了示例, 如下; 其实, 和上面UI线程中Looper的处理过程差不多, 只是由于UI线程的特殊性, 所以单独给它准备了一个方法~(实际上是做一些其他特殊判断和处理)
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发送消息时, 可以使用两个系列, 一个是postXXX系列, 一个sendXXX系列
postXXX: post系列主要是发送一个自定义的Runnable事件, 然后去执行(延迟执行)自定义事件
| post(Runnable r) |
| postAtTime(Runnable r, long uptimeMillis) |
| postAtTime(Runnable r, Object token, long uptimeMillis) |
| postDelayed(Runnable r, long delayMillis) |
| postDelayed(Runnable r, Object token, long delayMillis) |
| postAtFrontOfQueue(Runnable r) |
sendXXX: send系列主要是发送封装的一个msg, 然后根据msg带的tag或者数据在handleMessage()中进行处理
| sendMessage(Message msg) |
| sendEmptyMessage(int what) |
| sendEmptyMessageDelayed(int what, long delayMillis) |
| sendEmptyMessageAtTime(int what, long uptimeMillis) |
| sendMessageDelayed(Message msg, long delayMillis) |
| sendMessageAtTime(Message msg, long uptimeMillis) |
| sendMessageAtFrontOfQueue(Message msg) |
但是, 实际上post系列最终还是将Runnable事件封装为了一个Message对象交给了send系列处理, 而不管是postXXX还是sendXXX, 最终转到的都是enqueueMessage()进行处理; 另外, 不管是post的Runnable还是send的int what等其他类型, 其实最终都会将每一条消息封装为一个Message对象进行传递
四. 消息存储
前面我们说过, MessageQueue会对发送的消息进行存储, 这一步就是在enqueueMessage()中处理的; 如下; 需要注意的是这里传入的MessageQueue是在创建Handler时创建, 也就是说和Handler相关, 因为涉及到多线程的处理, 所以需要清楚MessageQueue的来源, 不然消息处理的时候就混了~
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里我们简单看一下MessageQueue中Message的组织方式, 如下, MessageQueue.enqueueMessage(), 可以看出是很明显的单链表的存储方式, 之所以用单链表, 是因为单链表在增加和删除节点上有优势
boolean enqueueMessage(Message msg, long when) {
...
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
...
}
五. 消息处理
还记得前面我们还有一个问题没有解决吗, 就是Looper.loop()过程, Looper.loop()过程实际上就是开启循环处理消息的过程~
该函数如下; 首先获取消息处理线程的Looper, 前面我们已经看到了, Looper.loop()是在消息处理线程中调用的, 所以这里获取到的, 也就是消息处理线程, 即创建Handler的线程; 之后获取对应线程的消息队列, 即MessageQueue, 需要注意这里的MessageQueue和Looper关联的, 而Looper的获取是通过Thread.currentThread()来判断和获取的, 那么这里就能获取到和对应Handler相关联的MessageQueue, 当我们在不同线程中调用Handler.sendMsg的时候, 插入的也是和该Handler关联的MessageQueue, 这样, 一整条完整的线就串成了: 不同的线程往需要发送消息的线程的MessageQueue中插入Message, 而和线程关联的Looper又从该MessageQueue中不断获取Message处理, 这样就完成了线程的切换 !!
接下来就是用一个死循环(for)开始消息处理了; 逻辑也比较简单, 其实就是不断的从MessageQueue中获取消息(MessageQueue.next()), 然后回调Handler.dispatchMessage(msg)分发, 需要注意的是, 这里的msg.target实际上是存储的对应Handler的引用
public static void loop() {
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block
// 如果消息为null, 不处理咯
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
msg.target.dispatchMessage(msg);
}
}
我们看一下dispatchMessage的处理逻辑, 如下; 逻辑也很简单, 就是消息处理的优先级: 如果Message自身设置了Callback, 就直接执行Message的Callback(这种情况对应前面说的post(Runnable)的情况); 如果构造Handler的时候设置了Callback那么交给该Callback处理, 否则回调handleMessage()处理
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这里还要注意的一点是从MessageQueue中获取消息时, 即MessageQueue.next(), 这是一个阻塞方法, 也就是说当MessageQueue中没有消息的时候, 会阻塞在这里, 直到MessageQueue中再次存储消息
六. 总结
到这里Handler的整个Java层面的运行机制我们就讲解完啦~ , 到这里我们对整个Android的消息传递和处理机制也有了比较详细的了解了; 为加深记忆, 可以对着源码自己再过一遍过程~