介绍
前面的博客已经简要分析了Handler ,Looper ,MessageQueue 的大体框架,我们已经知道,Handler 主要作用是封装Message 的发送和接收,Looper 的功能是不断轮询MessageQueue ,取出Message 交给对应的Handler 执行,而消息队列的休眠,唤醒,及我们比较少用到的IdleHandler ,同步屏障等功能都是由MessageQueue 实现的,所以下面就来详细分析上述功能是如何实现的。
IdleHandler
首先是IdleHandler ,IdleHandler 是一个只有一个方法的接口:
public static interface IdleHandler {
boolean queueIdle();
}
queueIdle 方法会在MessageQueue 中当前没有消息需要执行(Message 空了或者下一个Message 还要等一会儿才到执行时间)之后回调,方法的返回值是一个boolean 值:当返回值为false 时,MessageQueue 会删除掉这个IdleHandler ,之后不会再调用到这个对象。反之则继续持有这个对象,在下次空闲时再调用一次。 为MessageQueue 添加IdleHandler 的示例代码如下:
MessageQueue.IdleHandler idleHandler = () -> false;
Looper.myQueue().addIdleHandler(idleHandler);
然后MessageQueue 会把idleHandler保存在mIdleHandlers 列表里
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
最后在next 方法中,没有message可执行的情况下调用:
Message next() {
.....
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
.....
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null;
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
.....
}
}
使用方法示例
我在工作中还没有使用过IdleHandler ,但源码中有一处地方用到了:在ActivityThread 中通过IdleHandler 来在空闲时调用GC,来减少卡顿的可能性:
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
purgePendingResources();
return false;
}
}
同步屏障
同步屏障同样是一个我们几乎不会用到的机制,我搜索源码中也只有ViewRootImpl 类(实现 View 的绘制的类)和几个测试类中用到。 需要了解同步屏障,需要先知道同步是什么,这里的同步并不是值多线程中线程安全的那个同步,而是指,一个MessageQueue 中的所有message 的执行顺序的同步,即默认情况下所有message 都是同步的,所有message 按when 排序,依次执行。而如果某些message 优先级非常高,我们想让它在特定时间后尽早执行,应该怎么办呢?这时候就是同步屏障的用武之地了。 同步屏障功能的实现分为两个部分:1:插入一个同步屏障,使同步屏障之后的同步message 暂停执行;2:插入异步message 。 首先看插入同步屏障的方式,我们插入同步屏障的方法是调用MessageQueue#postSyncBarrier 方法:
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) {
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
public void removeSyncBarrier(int token) {
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
postSyncBarrier 会按照when 的值,在消息队列中插入一个target 为null的message ,因为when 的默认值是SystemClock.uptimeMillis() 当前时间,所以可以想象,这个同步屏障大多数时候都会插入到队列头部。同时需要注意,这个方法是hide 的,所以我们正常情况下调用不了。postSyncBarrier 会返回一个token,后续可以通过removeSyncBarrier 方法并传入这个token来删除相应的同步屏障。 往消息队列中插入了同步屏障后,会在next 方法中检查到这个message :
for (;;) {
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
}
当MessageQueue 在任务队列中检查到同步屏障后,会越过后续所有同步信息,找到一个异步信息并返回,如果没有找到异步信息,就设置nextPollTimeoutMillis 为-1,进入等待状态(这里需要补充为什么-1是等待状态)。也就是说,同步屏障不仅会使得同步message 优先执行,在同步屏障被removeSyncBarrier 方法移除之前,都会持续阻塞异步message 执行。 同步屏障目前最重要(或者说sdk中唯一)的使用方式是在UI刷新的时候防止主线程的其他message 阻塞,其他的用处确实还没想到,平常似乎没有必要使用异步Handler。
native方法实现分析
MessageQueue 中有部分方法是通过native 方法的方式来实现的:
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
@UnsupportedAppUsage
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
以上方法的实现在frameworks/base/core/jni/android_os_MessageQueue.cpp。接下来就来逐个方法分析它们的实现。
nativeInit
nativeInit 在构造方法中调用,对应的函数是android_os_MessageQueue_nativeInit :
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
android_os_MessageQueue_nativeInit 中初始化了NativeMessageQueue ,然后将NativeMessageQueue 的引用传回,即mPtr 来保存,后续其他native 方法就需要将mPtr 作为参数,调用NativeMessageQueue 的实现。
nativeDestroy
private void dispose() {
if (mPtr != 0) {
nativeDestroy(mPtr);
mPtr = 0;
}
}
static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->decStrong(env);
}
nativeDestroy 方法对应的函数是android_os_MessageQueue_nativeDestroy ,作用就是释放nativeMessageQueue 的引用。dispose 会在finalize 方法中或quit 方法调用后的next 方法中执行。插一句话,这是我第一次见到finalize 的使用,看来这个方法的作用确实是用来管理C++相关的资源。
nativePollOnce
nativePollOnce 用于阻塞MessageQueue ,在next 方法中调用,参数为nextPollTimeoutMillis ,阻塞时间,-1时表示一直阻塞,直到调用nativeWake 唤醒。这个方法对应的函数是android_os_MessageQueue_nativePollOnce :
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
android_os_MessageQueue_nativePollOnce 调用到NativeMessageQueue::pollOnce ,然后调用到Looper#pollOnce 函数。
nativeWake
nativeWake 方法用于唤醒阻塞的MessageQueue ,这个方法会在quit 方法中(唤醒队列来退出)、removeSyncBarrier 方法中(唤醒队列来处理同步信息)、enqueueMessage 方法中(原本信息队列为空,或者这是第一个异步信息)有调用,它的实现是android_os_MessageQueue_nativeWake :
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
android_os_MessageQueue_nativeWake 调用到NativeMessageQueue::wake ,然后调用Looper#wake 函数。
总结
从本节内容可以看出,NativeMessageQueue 只是起到了一个封装的作用,重要的消息队列的阻塞和唤醒功能都是通过native层的Looper类来实现的,关于这个类,我留到下一篇博客再讲,MessageQueue的内容就到此为止了。
|