前言
Handler 部分的讲解计划分两篇博客讲完实现原理,一篇主要介绍 Java 层的实现,另一篇介绍 Native 相关的实现,本篇介绍前者。
讲解完实现原理之后,会再新开几篇博客讲解其在系统源码中的主要使用场景等相关内容。
本篇主要内容有:
- MessageQueue 的基本运作流程,包括入队、出队、消息处理等实现原理
- 同步屏障和异步消息
- IdleHandler
- Message池管理
如果这篇文章对你有帮助,请关注点赞加收藏。
还可以关注我的微信公众号"ZZH的Android",还有更多 Android 系统源码解析的干货文章等着你。
1、Handler 作用
(1) 在线程之间互发消息,实现线程间通信
(2) 发送延时消息,延时执行任务
2、类图
3、Handler-Looper-MessageQueue 模型
整个模型的运行步骤如下:
- 创建 MessageQueue 队列
- 将 Message 对象入队
- 将 Message 对象出队
- 处理出队的 Message 后面跟代码流程时,可以跟着这个图来看,方便理解。
以上运行步骤依赖于 Handler、Looper 和 MessageQueue 三个核心类实现。
4、实现原理
在 Looper.java 文件的开头,给了一段 Looper 使用的示例代码,如下
java
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
// 创建MessageQueue对象
Looper.prepare();
mHandler = new Handler(Looper.myLooper()) {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
// 启动MessageQueue的循环运转
Looper.loop();
}
}
其中,Looper.prepare()创建 MessageQueue 对象;
Looper.loop()启动 MessageQueue 队列的循环运转;
Handler 负责发送消息和处理消息。
此处需要注意的是,子线程中并不是必须要创建消息处理机制的这几个对象的,只有当有业务需要才创建即可。
主线程必须创建,而且是由系统在进程启动时创建的,不需要 App 开发者再手动创建。
Looper 给主线程单独定义了专门的方法 prepareMainLooper,代码位置如下:
java
// frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
......
// 创建主线程的MessageQueue
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// 启动MessageQueue循环运转
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
4.1 Looper.prepare()->MessageQueue 的创建
当 Looper 执行其 prepare()函数时会创建 MessageQueue 对象。MessageQueue 是一个包含 Message 元素的队列, 从其名字翻译为队列,实际上是一个单向链表,因为每个 Message 对象中的 next 成员会指向下一个 Message 对象。
创建 MessageQueue 代码的如下:
java
// frameworks/base/core/java/android/os/Looper.java
public static void prepare() {
// 这里的true表示MessageQueue队列可以手动停止运转。
// 调用Looper的quit方法可以停止运转。
prepare(true);
}
// 注意这个函数是private方法,App无法使用。说明子线程的
// quitAllowed一定为true。
// 这里使用了ThreadLocal,表明每个线程都有专属的一个唯一的Looper对象
private static void prepare(boolean quitAllowed) {
// 如果重复执行prepare方法,就会提示错误。
// 说明每个线程只能创建一个Looper对象
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 创建Looper对象,并保存到sThreadLocal里,
// 这里quitAllowed=true。
sThreadLocal.set(new Looper(quitAllowed));
}
可以通过 Looper 的 myLooper()函数获取当前线程的 Looper 对象:
java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
再看下给主线程使用的 prepareMainLooper 函数:
java
// 这个方法不允许应用再调用
public static void prepareMainLooper() {
// 这里传入了false,说明主线程的消息循环不允许被停止。
// 原因是主线程中有很多核心的功能是由Handler实现的,
// 比如Activity生命周期的回调。
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 主线程的Looper对象也会保存在sMainLooper中,
// 这样的获取,获取主线程的Looper对象就不用再从
// sThreadLocal 中拿了,可以节省时间,提高性能。
// 当需要创建基于主线程的Handler对象时,可以直接
// 使用Looper的getMainLooper方法获取主线程Looper对象。
sMainLooper = myLooper();
}
}
Looper 的构造函数
java
// 创建MessageQueue对象,保存在mQueue变量中;
// 并且将当前线程保存在mThread变量中。
// 这里可以看到quitAllowed最终传递给了MessageQueue
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
下面看下 MessageQueue 的构造函数
java
// frameworks/base/core/java/android/os/MessageQueue.java
MessageQueue(boolean quitAllowed) {
// 是否可以被手动停止
// 主线程必须为false
// 子线程默认为true,也只能为true
mQuitAllowed = quitAllowed;
// 创建NativeMessageQueue对象,并保存其引用。
mPtr = nativeInit();
}
下面看下 nativeInit()的实现。
c++
// frameworks/base/core/jni/android_os_MessageQueue.cpp
// 可以看到nativeInit()对应实现为android_os_MessageQueue_nativeInit
static const JNINativeMethod gMessageQueueMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
......
};
// 看下android_os_MessageQueue_nativeInit实现
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
// 创建NativeMessageQueue对象
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
// 增加强引用计数,确保该对象不会被释放
nativeMessageQueue->incStrong(env);
// 将nativeMessageQueue的指针返回到java层。
return reinterpret_cast<jlong>(nativeMessageQueue);
}
// 下面再看下NativeMessageQueue的构造函数,
// 创建了Native的Looper对象。
// Native Handler相关的另起一篇文章单独介绍
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
再看下 Looper.quit 方法,这个方法是用来结束消息队列处理流程的。
可以看到有两个方法,最终都调用了 mQueue.quit,传递的参数不同而已。
java
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
MessageQueue 的 quit
java
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
// 将mQuitting为true,这个变量在MessageQueue循环处理消息时
// 会做判断,如果是true,则停止 循环处理。
mQuitting = true;
// 参数的不同,会分别调用下面的两个方法。
// 这两个方法涉及到队列的操作,后面我们讲完入队和出队后
// 再分析下面两个函数的实现。
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
// 唤醒线程,这样就可以循环起来然后拿到mQuitting做判断退出。
nativeWake(mPtr);
}
}
小结
- Looper 的静态方法 prepare 函数里面创建了 Looper 对象,并且将 Looper 对象保存在了 sThreadLocal 中,这样可以保证每个线程都有一个自己的 Looper 对象。
- 主线程有特殊的创建 Looper 对象的方法 prepareMainLooper,并且创建完成后除了保存在 sThreadLocal 中外,还保存在了一个静态变量 sMainLooper 中。
- Looper 有个 MessageQueue 类型成员 mQueue,在 Looper 的构造函数中进行创建,最终创建了一个 Native 对象 NativeMessageQueue,并且 java 层拿到了它的指针。
- Looper 的 quit 和 quitSafely 方法可以停止消息循环,其最终调用的都是 MessageQueue 的 quit 方法。
- MessageQueue 有个成员 mQuitAllowed,由 Looper 的私有方法 prepare 传入,表示消息循环是否可以通过调用 quit 被停止。子线程可以被停止,主线程不能被停止。
可以推测出的结论
- 每个线程最多只有一个 Looper 对象和一个 MessageQueue 对象。
上面讲完 MessageQueue 队列的创建,下面开始讲入队和出队。
4.2 MessageQueue 入队操作
入队操作由 Handler 完成。
4.2.1 Handler 的创建
java
// frameworks/base/core/java/android/os/Handler.java
/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*
* @deprecated Implicitly choosing a Looper during Handler construction can lead to bugs
* where operations are silently lost (if the Handler is not expecting new tasks and quits),
* crashes (if a handler is sometimes created on a thread without a Looper active), or race
* conditions, where the thread a handler is associated with is not what the author
* anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper
* explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or
* similar. If the implicit thread local behavior is required for compatibility, use
* {@code new Handler(Looper.myLooper())} to make it clear to readers.
*
*/
@Deprecated
public Handler() {
this(null, false);
}
以上不带参数的默认构造函数已经被弃用,注释里写了一堆原因,总结就是这么用会带来很多问题。如果要用,就显式指定其依赖的 Looper 对象。
java
/**
* Use the provided {@link Looper} instead of the default one.
*
* @param looper The looper, must not be null.
*/
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
/**
* Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages.
*
* @param looper The looper, must not be null.
* @param callback The callback interface in which to handle messages, or null.
*/
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
this(looper, callback, false);
}
上面两个构造函数最终都会到下面的 hide 方法。
java
/**
* @hide
*/
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
// 下面三个主要变量赋值,比较好理解
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
//是否为异步消息
mAsynchronous = async;
}
- 第一个 Looper 参数是 NonNull,通过 Looper.myLooper()方法获取,如果没有调用过 Looper 的 prepare 方法,则会报错。
- 第二个参数 Callback 是 Handler 的内部接口,如果传入了这个参数不是空,则消息出队后将不会执行 Handler 的 handleMessage 方法,而是会执行实现了 Callback 接口对象的 handleMessage 方法。这样的话就不需要重写 Handler 的 handleMessage 了。
java
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
- 第三个参数表示,由这个 Handler 发送的消息是否为异步消息。
异步消息
上面 Handler 的构造函数中,其中最后一个参数需要特别注意一下。
它表示由这个 Handler 发送的 Message 消息是否为异步消息。
如果参数是 false,则表示此 Handler 发送的消息都是同步消息;如果参数是 true,则表示此 Handler 发送的消息都是异步消息。
从上面 Handler 的构造函数可以看出,普通应用的话这个参数默认为 false,也就是只能创建同步消息。
对于系统进程,则可以直接调用,指定 Handler 发送的消息是同步还是异步消息。
这里第一次出现异步消息这个概念,我们现在先理解为异步消息会比同步消息优先执行。
普通应用如何创建异步消息的 Handler
那么普通应用有办法创建异步消息的 Handler 吗?答案是有。
Handler 提供了两个静态方法,如下: 可以看到其最后一个参数为 true,表示异步。
java
@NonNull
public static Handler createAsync(@NonNull Looper looper) {
if (looper == null) throw new NullPointerException("looper must not be null");
return new Handler(looper, null, true);
}
@NonNull
public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
if (looper == null) throw new NullPointerException("looper must not be null");
if (callback == null) throw new NullPointerException("callback must not be null");
return new Handler(looper, callback, true);
}
关于异步消息我们先讲这么多,后面在用到的时候会继续讲解。
4.2.2 Handler 发送 Message 入队
Handler 有一系列的 postXXX 和 sendXXX 函数,最终都会调用到 sendMessageAtTime 函数。
不同的方法给下面的函数的两个参数赋值不同而已。
uptimeMillis:表示消息的延时时间,如果函数不带 Delay 或者要插入队头的则赋值为 0;
msg: Message 对象,调用不同的 post 或者 send 方法,对其不同成员进行赋值。
java
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
// mQueue是构造Handler时由传入的Looper对象提供。
MessageQueue queue = mQueue;
// 如果没有MessageQueue对象,直接报异常退出。
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
// 继续执行Message入队操作
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// Message类中有个target成员,其类型为Handler,
// 这里将其赋值为当前Handler对象,后续消息出队后
// 可能交给Handler处理消息。
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
// 是否为异步消息,这里可以看到,如果构造Handler时mAsynchronous为true,
// 则将Message设置为异步标志。这样的话使用这个Handler发送的Message将
// 都是异步Message。
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 调用MessageQueue的enqueueMessage方法入队
return queue.enqueueMessage(msg, uptimeMillis);
}
上面使用到了 Message 的 setAsynchronous 方法,我们看下其实现:
java
// frameworks/base/core/java/android/os/Message.java
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
同时 Message 还提供了一个判断自己是否为异步消息点方法:
java
// frameworks/base/core/java/android/os/Message.java
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}
FLAG_ASYNCHRONOUS 的值:
java
// frameworks/base/core/java/android/os/Message.java
static final int FLAG_ASYNCHRONOUS = 1 << 1;
消息入队
这里要敲黑板了,第一个重点部分来了。
MessageQueue 的入队操作,其实就是按照 Message 的延时执行时间参数 when 来进行排序入队。
java
// frameworks/base/core/java/android/os/MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
// 通过Handler进行入队的话这个参数是不会为空的。
// 这里要注意一下,并不是所有的在队列中的msg的target
// 都不是null,有一类Message叫做同步屏障,它的target就必须为null,
// 只不过它不是通过这个方法进行入队的。所以后面我们看到代码还有
// msg.target == null的判断时不要惊讶。至于什么是同步屏障,
// 后面会讲到的。
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 入队一个Message时加锁
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
// 如果此时调用了Looper的quit函数,则说明取消了MessageQueue队列的运转,直接退出。
// 这里说明,当quit后,将不能再入队新的Message。
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
// 释放msg
msg.recycle();
return false;
}
// 标记为正在使用状态
msg.markInUse();
// when就是此msg延时多久执行
msg.when = when;
// mMessage初始为空,表示的是队列的队头
Message p = mMessages;
// 当消息入队但是还未到其执行时间时,线程会进入阻塞,
// 这里的needWake用来判断是否需要唤醒线程进行出队
boolean needWake;
// 这里的三个条件满足其一都会将当前Message插入队头
// p == null,说明当前队列为空,新来的Message自然作为队头
// when = 0,说明此Message没有延时需要立即执行,所以也放在队头
// when < p.when,说明当前Message的延时小于队头Message的延时,所以放在队头
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
// 这里的mBlocked表示MessageQueue的出队函数next()是否阻塞。
// 如果是阻塞状态,则队头Message变化时要将其唤醒,表明这个时候
// 可能有消息需要出队处理了,特别是当when == 0 的时候。
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
// 这里的p实际上是队头的Message,但是这里对其target == null做了判断,
// 但是该函数的开头却有target == null的话会直接抛异常退出,那么这里会是null吗?
// 除非有一种可能,那就是这个target为null的Message不是通过本方法入队的。
// 这里又引入了一个新的概念:同步屏障(synchronization barriers),
// 同步屏障我们先理解为一个target为null的Message,下面会详细介绍。
// 如果队头是一个同步屏障且当前入队的消息是异步消息,且mBlocked为true,则
// 需要唤醒当前线程来。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
// 下面的操作是真正入队的操作,从队头开始遍历
Message prev;
for (;;) {
prev = p;
p = p.next;
// p == null 表示遍历到了队尾
// when < p.when 表示队列按延时时间排序
// 延时时间越小的越靠前,越靠近队头
if (p == null || when < p.when) {
break;
}
// 如果队列中有异步消息,则将needWake置为false。
//
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// 将当前msg插入队列,插入p的前面
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
// 如果needWake为true,则唤醒线程
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
同步屏障
上面我们又遇见了一个新的概念:同步屏障,或者叫做同步屏障机制。在这个机制中,包含一个 target 为 null 的 Message,这个 Message 并不是通过上面的 enqueueMessage 方法入队的,而是另有他法。
插入同步屏障 Message 的方法 MessageQueue 的 postSyncBarrier
java
// frameworks/base/core/java/android/os/MessageQueue.java
/**
* Posts a synchronization barrier to the Looper's message queue.
* @hide
*/
@UnsupportedAppUsage
@TestApi
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
// mNextBarrierToken 表示每个同步屏障Message的token,
// 初始值为0,从1开始记录
final int token = mNextBarrierToken++;
// 这里队Message赋值,可以看到并没有给target赋值,所以msg.targt=null
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
// 将同步屏障msg入队,这里按照when的大小排序入队
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
// 将msg插到p的前面
msg.next = p;
prev.next = msg;
} else {
// msg插入队头的情况
msg.next = p;
mMessages = msg;
}
// 返回值为msg的token
return token;
}
}
移除同步屏障 Message 的方法 removeSyncBarrier(int token), 参数为 postSyncBarrier 时返回的 token 值。
java
/**
* Removes a synchronization barrier.
*
* @param token The synchronization barrier token that was returned by
* {@link #postSyncBarrier}.
*
* @throws IllegalStateException if the barrier was not found.
*
* @hide
*/
@UnsupportedAppUsage
@TestApi
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
// 如果p为空,说明没找到需要移除的同步屏障Message
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
// 如果p != null,则说明p就是需要移除的msg
final boolean needWake;
if (prev != null) {
// 要移除的msg不是队头
prev.next = p.next;
needWake = false;
} else {
// 要移除的msg是队头
mMessages = p.next;
// 队头发生变更,且队头是同步消息,needWake为true
needWake = mMessages == null || mMessages.target != null;
}
// 释放移除掉的Message p
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
到目前为止,我们已经接触并分别简单介绍了同步屏障和异步消息两个概念了。
他们是两个 Message 对象,同步屏障的特点是 msg.target=null,异步消息的特点是 isAsynchronous 为 true。
那么这两者的作用究竟是什么呢?其实他们两个是一起发生作用的,或者说,同步屏障的作用是为了保证异步消息可以优先执行。当出队遇到队头为同步屏障时,则其后面的同步消息将不会被执行,只会执行异步消息,除非将同步屏障移除。
需要说明的是,添加和移除同步屏障是 hide 方法,只能系统进程使用。
下面的出队操作将会看到同步屏障和异步消息的处理逻辑和其作用。
4.3 MessageQueue 的流转-出队
4.3.1 Looper.loop()
Looper.loop()方法执行后消息队列开始运转,按规则进行出队操作。
java
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
@SuppressWarnings("AndroidFrameworkBinderIdentity")
public static void loop() {
// 对Looper进行检查
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
// loop开始后设置mInLoop为true
me.mInLoop = true;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
// 这里可以通过属性设置一个消息处理时间的阈值,如果消息处理超过这个时间,将会
// 有警告级别日志打印,默认这个属性是没有的,调试的时候可以自己设置这个属性来
// 测试消息处理时间。
// Looper是有专门的接口设置这个时间阈值的,如果同时设置了这个属性值,那么Looper接口
// 设置的值会被这个属性值覆盖,后面代码可以看到。
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
// 这个值默认为false,表示msg的投递时间未超过设置的阈值
me.mSlowDeliveryDetected = false;
// 这里进入了for循环,如果loopOnce返回false,则会退出循环,
// 也就表示MessageQueue不工作了,除非再次调用loop方法,
// 当调用quit方法后,loopOnce将会返回false
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
下面看下 loopOnce 方法的实现
java
/**
* Poll and deliver single message, return true if the outer loop should continue.
*/
@SuppressWarnings("AndroidFrameworkBinderIdentity")
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
// 执行MessageQueue的next()函数,返回下一个将要处理的Message,
// 如果下一个Message时间未到,next()方法则会阻塞等待。
// 这个函数下面会详细分析。
Message msg = me.mQueue.next(); // might block
// 如果调用了Looper的quit方法,那么next()函数将会返回null。
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
// This must be in a local variable, in case a UI event sets the logger
// 这里是打印Message的处理日志的,这里的mLogging可以通过Looper的
// setMessageLogging方法设置,默认为null。
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what);
}
// Make sure the observer won't change while processing a transaction.
// sObserver可以通过Looper的setObserver方法设置,是用来监听回调
// Message的处理状态的。
final Observer observer = sObserver;
// Looper的setTraceTag方法可以设置mTraceTag
// 用来记录trace信息。此方法为hide方法,普通应用无法使用。
final long traceTag = me.mTraceTag;
// Looper的setSlowLogThresholdMs方法用来设置这两个值,也是hide方法。
// 到这里需要解释一下这两个值的含义了。
// mSlowDispatchThresholdMs: 消息时间的阈值,或者理解为处理完一个消息的时间阈值。
// 如果消息处理时间比这个大,说明消息处理的比预期慢了,可能发生了异常。
// mSlowDeliveryThresholdMs: 表示消息投递的时间差阈值,比如我的Message是延时500毫秒
// 执行的,实际开始处理的时间(后面的dispatchStart)是550毫秒后,这就差了50毫秒,如果我们
// 设置的阈值小于50,就说明消息投递的时间误差超出预期,可能发生异常。
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
// thresholdOverride前面我们讲过,是调试时需要手动设置的属性值。
// 如果这个值大于0,则覆盖setSlowLogThresholdMs方法设置的两个值。
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
// 是否打印警告日志的开关
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
// 记录开始处理msg的trace
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
// 记录消息派发的开始时间
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
// observer回调开始派发消息
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
// 执行Handler的dispatchMessage函数
msg.target.dispatchMessage(msg);
if (observer != null) {
// observer回调派发消息结束
observer.messageDispatched(token, msg);
}
// 记录消息派发的结束时间
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
// observer回调派发消息发生异常
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
// 记录处理完成msg的trace
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
// 前面说过,mSlowDeliveryDetected初始化为false
if (me.mSlowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
me.mSlowDeliveryDetected = false;
}
} else {
// showSlowLog函数用来计算dispatchStart-msg.when是否大于slowDeliveryThresholdMs,
// 如果是,则打印警告日志。
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
// 并且将mSlowDeliveryDetected赋值true,用来表示队列中有一个消息投递的慢了,
// 直到遇见下一个投递时间差没有超出10毫秒,再将其赋值false,也就是上面的if条件语句。
me.mSlowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
// 如果消息派发处理时间超出阈值,打印警告日志。
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
// 如果我们设置了Printf,则打印处理完毕的日志
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
// 对已经处理完的消息对象进行回收
msg.recycleUnchecked();
return true;
}
上面有两个方法我们没有展开将,一个是 MessageQueue 的 next()方法,一个是 Handler 的 dispatchMessage 方法,下面开始讲解。
4.3.2 MessageQueue 的 next()
需要注意的是,此时的 MessageQueue 里面的 Message 已经按照其延时时间 when 从小到大排序完成,when 越小越靠前靠近队头。
java
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
// 调用quit方法后,mPtr会赋值0,这里就会返回null,结束队列的循环运转。
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// 这里又出现一个新名词,IdleHandler
// 后面会详细解释
int pendingIdleHandlerCount = -1; // -1 only during first iteration
// 下一个消息执行还需要等待的时间
int nextPollTimeoutMillis = 0;
// 使用一个for循环寻找下一个要执行的Message,
// 找到后将Message返回。
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 这里是线程阻塞时间
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
// 获取当前时间
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 如果遇到同步屏障,则其后面的同步消息将都会略过不执行,
// 只执行异步消息,除非调用removeSyncBarrier移除同步屏障,
// 这种情况只有当同步屏障成为队头时,也就是如果同步屏障前面有
// 同步消息,则会先执行前面的同步消息。
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// 找到了要执行的msg
if (msg != null) {
// 如果当前时间now还小于msg的执行时间,则计算下还需要等待的时间
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 {
// Got a message.
// 否则msg已经到了可以执行的时间,需要返回
// 同时mBlocked置为false
mBlocked = false;
if (prevMsg != null) {
// 走到这里说明找到的是异步msg
// 从队列中移除找到的msg
prevMsg.next = msg.next;
} else {
// 走到这里说明找到的msg是队头,
// 将msg的下一个元素置为新的队头。
mMessages = msg.next;
}
// 这里将msg标志为正在使用,然后返回。
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 如果msg为null,说明队列中没有要执行的消息了,
// 或者此时表头为同步屏障,但是队列中没有异步消息
// No more messages.
// 阻塞,直到下个Message入队
nextPollTimeoutMillis = -1;
}
// 如果在运行过程中,有地方调用了Looper的quit方法,则退出loop循环操作。
// 此后所有的msg都将不会执行,除非再次调用Looper.loop()启动运行
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
// dispose函数用于释放
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
// 从这里开始都next()函数结束,剩下的代码都是IdleHandler相关的了。
// 请注意,这里的代码还在上面的for循环里,for循环开始前pendingIdleHandlerCount赋值了-1。
// 所以,第一次for循环 或者 队头msg为空(mMessages == null)
// 或者队头的msg还未到执行时间(now < mMessages.when),
// 就以mIdleHandlers的size重新赋值pendingIdleHandlerCount。
// 其中mIdleHandlers是IdleHandler的一个列表,初始为空。
// IdleHandler是一个接口类型,其有一个方法boolean queueIdle().
// 列表mIdleHandlers需要通过MessageQueue的addIdleHandler方法添加元素,
// 通过MessageQueue的removeIdleHandler方法删除元素。
// IdleHandler存在的意义是当MessageQueue空闲时可以执行一些额外的任务。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// 如果pendingIdleHandlerCount<=0,说明没有需要执行的IdleHandler
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
// mPendingIdleHandler是IdleHandler类型的数组,其最小长度为4。
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
// 将mPendingIdleHandlers中的元素赋值到mPendingIdleHandlers数组中
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
// 遍历mPendingIdleHandlers数组,执行其queueIdle()函数。
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
// 如果queueIdle()函数返回false,则将其IdleHandler从列表中移除。
// 这样下次执行next()时,将不会再执行这个IdleHandler的queueIdle()方法
// 否则若queueIdle()函数返回true,则下次next()时就还会执行其
// queueIdle()函数。
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 到这里,如果有IdleHandler的话,则已经执行完一轮。
// 此时需要重置pendingIdleHandlerCount = 0
// 这样下次for循环时,就不会再执行其queueIdle函数了。
// 下次执行要在下一次next()函数执行时继续按照上面的规则判定是否执行。
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
// 走到这里说明队列空闲时执行了额外的IdleHandler任务,此时可能已经到了下一个Message需要
// 执行的时间,所以将休眠唤醒时间赋值为0,也就是下次for循环时直接唤醒线程。
nextPollTimeoutMillis = 0;
}
}
4.3.3 Handler 的 dispatchMessage
dispatchMessage 是 Message 出队后的最后一步,进行消息处理。
java
public void dispatchMessage(@NonNull Message msg) {
// 如果是通过post Runnable发送的消息,则会回调Runnable的run方法
if (msg.callback != null) {
handleCallback(msg);
} else {
// 如果构造Handler时传入了Callback参数,则回调
// Callback的handleMessage方法
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//如果以上callback都没有,最后才是执行Handler的
// handleMessage方法
handleMessage(msg);
}
}
5、Looper quit
最后分析一下 quit 方法的实现,前面讲过,quit 有两个方法,分别是 quit() 和 quitSafely(),其最终会调用到 MessageQueue 的 quit 方法中
java
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
5.1 removeAllMessagesLocked
这个比较简单,遍历队列,对每个 Message 进行回收,最后将队头 mMessages 赋值为 null。
java
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
5.2 removeAllFutureMessagesLocked
这个函数相比 removeAllMessagesLocked 多了一个 Future 字段,字面意思是未来,我们猜测这个函数移除的是还未到执行时间的 Message,如果 Message 已经到了执行时间,在投递阶段,还未派发完成,则需要执行完成。
下面看下实现跟我们猜测是否一样。
java
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
// 队头还未到执行时间,那所有Message都未到,
// 队列里的Message都属于Future Message,
// 直接调用removeAllMessagesLocked即可。
if (p.when > now) {
removeAllMessagesLocked();
} else {
//
Message n;
for (;;) {
n = p.next;
// 到这里说明,队列中的Message都在投递状态,
// 都不属于Future Message,所以直接return。
if (n == null) {
return;
}
// 找到了还未到执行时间的msg,
// 那么它后面的肯定也都是Future msg
// 退出遍历循环即可。
if (n.when > now) {
break;
}
//
p = n;
}
// 此时的n属于找到的第一个Future Message
// 此时p 是 n的前一个Message
// 下面要把p后面,从n开始的msg全部回收
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
6、 Message 池管理
最后有必要再看下 Message 的池管理。
有一个池子,里面原来是空的,当我们创建一个 Message 使用完进行回收后,并没有把这个 Message 对象销毁,而是放到了这个池子里,这个池子最大能容纳 50 个 Message 对象。当需要创建 Message 对象使用时,可以查看下池子是否为空,如果不是,就取一个拿出来使用。这样可以避免创建更多的 Message 对象,一定程度上达到节省内存和申请对象带来的开销。
Message 池也使用了单项链表的数据结构。
java
// frameworks/base/core/java/android/os/Message.java
// 下面几个变量是管理Message池子的一些工具变量
/** @hide */
public static final Object sPoolSync = new Object();
// sPool是池子的表头元素
private static Message sPool;
// 池子中的Message元素个数
private static int sPoolSize = 0;
// 最大能存放50个Message元素
private static final int MAX_POOL_SIZE = 50;
// Android 5.0之后这个值都为true
private static boolean gCheckRecycle = true;
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
// 如果池子不是空的,则将其表头Message拿出来返回
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 的回收函数
java
public void recycle() {
// 如果回收正在使用的msg,则抛出异常
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
// 回收过程中设置为正在使用状态
flags = FLAG_IN_USE;
// 成员变量值重置
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
// 如果当前线程池的Message数量小于MAX_POOL_SIZE,
// 则将当前回收的Message放入表头,sPoolSize递增
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}