Android开发中经常用到handler来发送消息,比如将消息抛到主线程去更新UI。handler机制涉及到四个核心的类:
- Looper:消息循环,有一个Messagequenue,不断从消息队列中取出消息;
- MessageQuenue:消息队列,里边包含Message;
- Message:消息,里边有一个Handler,负责处理该消息;
- Handler:管理消息队列,里边有Looper和MessageQueue;
为了更好的理解这三者的关系,我们可以想象成现实生活中,工厂里边的一条流水线。有一个放产品(Message)的传送带(MessageQuenue),被机器带动起来不断循环运行(Looper),而工人(Handler)需要将产品放到传送带上,然后传送到指定位置时再将产品进行下一步处理。
从上述表达的关系可以看出,有一个MessageQuenue,可以往里边仍Message,而Looper则要让MessageQuenue转起来,不断从里边取消息;而Handler负责把Message放到队列里边,最后再处理这个消息。
注:以下源码基于Android 9.0(版本名称: Pie API Level: 28)。
Looper类分析
frameworks/base/core/java/android/os/Looper.java
在该文件中给我们提供了一个实例:
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* // ①调用prepare
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
* // ② 进入消息循环
* Looper.loop();
* }
* }</pre>
上述使用了Looper的两个关键调用(①和②),下面逐一分析。
Looper准备:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
// 一个Looper只能调用一次prepare
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 构造一个Looper对象,设置到调用线程的局部变量中
sThreadLocal.set(new Looper(quitAllowed));
}
// sThreadLocal定义
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
ThreadLocal是一个泛型,是Java线程中的局部变量。全名是Thread Local Variable。具体可以自行研究源码:libcore/ojluni/src/main/java/java/lang/ThreadLocal.java
Looper构造函数:
private Looper(boolean quitAllowed) {
// 构造一个消息队列
mQueue = new MessageQueue(quitAllowed);
// 获取当前线程的Thread对象
mThread = Thread.currentThread();
}
prepare主要创建了Looper对象,并保存在调用线程的ThreadLocal中。同时Looper对象内部封装了一个消息队列。prepare通过ThreadLocal机制将Looper和调用线程关联。Looper循环:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
// 返回保存在调用线程的TLV中的Looper对象。
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 取出Looper的消息队列
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;
}
// ... 细节省略
try {
// 调用该消息的Handler,交给它的dispatchMessage函数处理
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// ... 细节省略
msg.recycleUnchecked();
}
}
/**
* 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的作用:
- 封装一个消息队列(Messagequenue);
- Looper的prepare函数将Looper和调用prepare的线程(即处理线程)绑定;
- loop函数启动一个无线循环来处理消息队列中的消息。
当事件源向这个Looper发送消息的时候,其实是把消息加到这个Looper的消息队列里了。那么,该消息就将由和Looper绑定的处理线程来处理。可事件源又是怎么向Looper消息队列添加消息的呢?下面需要了解Looper、Message、Handler的关系。
- Looper中有一个Message队列,里边存储的是一个个待处理的Message;
- Message中有一个Handler,这个Handler是用来处理Message的。
其中Handler类封装了很多琐碎的工作。具体:
frameworks/base/core/java/android/os/Handler.java
final Looper mLooper; // 有一个Looper
final MessageQueue mQueue; // 有一个消息队列
final Callback mCallback; // 有一个回调用的类
final boolean mAsynchronous; // 异步相关设置,本文不做分析
Handler有几个构造函数,主要区别是对上面三个重要成员变量初始化上。
// 构造函数1
public Handler() {
this(null, false);
}
// 构造函数2
public Handler(Callback callback) {
this(callback, false);
}
// 构造函数3
public Handler(Looper looper) {
this(looper, null, false);
}
// 构造函数4
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
// 构造函数5
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) {
// ... 细节省略
// 获得调用线程的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 得到Looper的消息队列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
上述构造函数中,Handler中的消息队列变量最终都会指向Looper的消息队列,Handler提供了一系列函数,帮助我们完成创建消息和插入消息队列的工作。这里列出一部分分析:
// 查看消息队列中是否有消息码是what的消息
public final boolean hasMessages(int what)
// 从Handler中创建一个消息码是what的消息
public final Message obtainMessage(int what)
// 从消息队列中移除消息码是what的消息
public final void removeMessages(int what)
// 发送一个只填充了消息码的消息
public final boolean sendEmptyMessage(int what)
// 发送一个消息,该消息添加到队列尾
public final boolean sendMessage(Message msg)
// 发送一个消息,该消息添加到队列头,所以优先级很高
public final boolean sendMessageAtFrontOfQueue(Message msg)
以sendMessage为了,其代码实现如下所示:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 把Message的target设为自己,然后加入到消息队列
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler的消息处理
刚才,我们往Looper的消息队列中加入了一个消息,按照Looper的处理规则,它在获取消息后会调用target的dispatchMessage函数,再把这个消息派发给Handler处理。
public void dispatchMessage(Message msg) {
// 如果Message本身有callback,则直接交给Message的callback处理
if (msg.callback != null) {
handleCallback(msg);
} else {
// 如果本Handler设置mCallback,则交给mCallback处理
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 最后才交给子类处理
handleMessage(msg);
}
}
通常情况下,我们一般都是采用第三种方法,即在子类中通过重载handleMessage来完成处理工作。
要理解Handler机制,主要是弄清楚Looper、MessageQuenue、Message、Handler之间的关系。为了便于理解,我们可以把这套框架想象成现实生活中工厂流水线,工人(Handler)拿到产品(Message),放在传送带(MessageQuenue)上,同时做了一个标记(msg.target = this;),传送带由机器带动(Looper),然后当机器将产品传递到另一位置时,该员工根据产品上的标记,负责处理该产品,分发到不同的地方。
上述分析细节可以自行查看Android源码,推荐网站:http://www.androidos.net.cn/sourcecode
扩展:
Looper类里边启动了一个for(;;)循环,如果消息队列时会不会一直占用CPU时间片?
循环中queue.next()函数里边用到了pipe/epoll机制,确保不会占用CPU时间片。之后会通过源码进行分析。