0 引言
Android消息机制的核心类为Handler、Looper、Message、MessageQueue
- Handler:名为Handler,它的主要功能就是处理消息,同时它还具有创建消息(Message),发送消息等功能
Looper:内部维护一个消息队列(MessageQueue),同时提供了进入/退出消息循环的功能。同时内部还有一个静态ThreadLocal对象,为每个线程都维护了一个Looper对象
Message:消息主体,封装了待处理的消息数据
MessageQueue:消息队列,负责维护消息
1 工作原理
下面我们从一个最简单的例子来分析消息机制的工作原理
Thread{
Looper.prepare()
Handler().post {
Toast.makeText(this@MainActivity, "I'm a Toast", Toast.LENGTH_SHORT).show()
}
Looper.loop()
}.start()
上述代码实现的功能很简单,其功能是在一个非主线程中显示一个Toast。主要分为三个步骤
- 调用Looper.prepare函数进行准备
- 创建Handler并调用其post函数发送消息
- 调用Looper.loop函数进入循环并开始处理消息
1.1 准备
Looper.prepare最终调用的是以下函数
private static void prepare(boolean quitAllowed) {//此时quitAllowed为true
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
该函数主要是创建了一个Looper对象,并保存在了sThreadLocal中。
sThreadLocal是一个ThreadLocal对象,通过这种方式,后续就可以通过Looper.myLooper函数获得线程对应的Looper对象了。
再来看看Looper的构造函数
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
创建了一个MessageQueue实例,同时获得当前线程的Thread对象
MessageQueue的构造函数也十分简单,仅仅是使用quitAllowed初始化了mQuitAllowed字段,代码如下
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//...
}
至此第一步的代码分析完毕
总结:第一步的主要工作是创建Looper以及MessageQueue
1.2 发送消息
第二步首先创建了一个Handler对象,由于内部调用了重载的构造函数,所以最终调用的代码如下
public Handler(Callback callback, boolean async) {//callback=null,async=false
//...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Looper.myLooper的代码十分简单,只是返回了sThreadLocal.get(),也即是在第一步中创建的Looper对象。
可以看到,Handler持有了Looper、MessageQueue对象的引用。
Handler创建完毕,接下来就调用了post函数
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
该函数首先通过getPostMessage函数生成了一个Message对象,然后再调用了sendMessageDelayed
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
该函数非常简单,通过Message.obtain函数获得一个Message对象,保存r至callback字段中,然后返回
getPostMessage函数经过若干函数调用最终会走到enqueueMessage函数中
private boolean enqueueMessage(MessageQueue queue,//Handler构造函数中保存的mQueue
Message msg,
long uptimeMillis) //uptimeMillis表示该消息应该被处理的时间点
{
msg.target = this;
if (mAsynchronous) {//消息机制中的异步机制,暂时先不管
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
函数首先保存当前Handler至Message的target字段中
然后调用了MessageQueue.enqueueMessage函数
//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
//一些前置检查
synchronized (this) {
//检查MessageQueue退出检查
msg.markInUse();//将Message标记为使用中
msg.when = when;
Message p = mMessages;//MessageQueue中使用链式结构来组织Message,每个Message都有一个next字段来指向下一个Message
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
// MessageQueue中无消息
// 或者when的值为0
// 或者when的值小于链表头部Message对象的话when字段值
// 那么将msg插入到链表的头部
msg.next = p;
mMessages = msg;
//...
} else {
//...
// 遍历链表,将msg插入到指定位置
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
//...
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
//...
}
return true;
}
enqueueMessage的功能简单,主要是负责将新的消息插入到消息链表中,插入时需要遵循when较小的Message排在前面,when较大的排在后面。
总结:第二步的核心功能就是通过Handler将Message插入到MessageQueue的消息链表中
1.3 进入消息循环
//Looper.java
public static void loop() {
final Looper me = myLooper();
//..
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;
}
//...
msg.target.dispatchMessage(msg);
//...
}
}
loop函数的脉络也是十分清晰的
- 通过调用MessageQueue.next函数获得需要处理的Message
- 调用Message.target(Handler类型)的dispatchMessage函数来处理消息
1.3.1 获取待处理的消息
//MessageQueue.java
Message next() {
//...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
//...
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//...
if (msg != null) {
// 对比当前时间与链表头部Message.when字段
if (now < msg.when) {
// 如果下一个消息的处理时间还没到,那么设置一个等待超时时间
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//region 拿到可用的msg,返回
// Got a message.
mBlocked = false;
if (prevMsg != null) {//prevMsg!=0的情况先不考虑
prevMsg.next = msg.next;
} else {
//链表头部移动到链表第二个元素上
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
//返回链表中的第一个元素
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
//...
}
//...
}
}
next函数的核心功能不复杂,首先判断当前时间与链表头部的Message.when字段的大小,如果该Message可以处理,那么直接返回链表头部的Message,否则继续等待。
1.3.2 处理消息
待处理的消息已经拿到了,那么接下来就是处理消息了
消息的处理还是通过Handler
代码中使用了msg.target.dispatchMessage来处理消息,Message中的target字段是在1.2小节的enqueueMessage函数中设置的,也即是发送该消息的Handler。
//Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);//该函数只有一句代码:msg.callback.run();
} else {
//...
handleMessage(msg);
}
}
dispatchMessage逻辑十分简单,当Message的callback字段不为空,使用handleCallback函数进行处理,否则使用handleMessage函数进行处理。
对于callback的处理比较简单,handleCallback中直接调用其run函数就结束了
handleMessage则需要我们自己重写去实现自定义的处理方式。
总结:调用Looper.loop函数进入死循环,循环内部不停地获取消息、处理消息
总结
本文以一个简单的例子为切入点,结合源码分析了Handler工作的基本流程
- 初始化消息队列(Looper、MessageQueue)
- 创建Message,并通过Handler将Message加入到MessageQueue的消息链表中
- 调用Looper.loop函数是当前线程进入死循环不停地从MessageQueue中获取Message,并使用Handler来处理消息