IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android消息机制(5)MessageQueue深入解析 -> 正文阅读

[移动开发]Android消息机制(5)MessageQueue深入解析

介绍

前面的博客已经简要分析了HandlerLooperMessageQueue的大体框架,我们已经知道,Handler主要作用是封装Message的发送和接收,Looper的功能是不断轮询MessageQueue,取出Message交给对应的Handler执行,而消息队列的休眠,唤醒,及我们比较少用到的IdleHandler,同步屏障等功能都是由MessageQueue实现的,所以下面就来详细分析上述功能是如何实现的。

IdleHandler

首先是IdleHandlerIdleHandler是一个只有一个方法的接口:

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; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                if (msg != null) {
                    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 {
                        .....
                        return msg;  //有待执行消息,返回给Looper
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

				//这部分代码是将mIdleHandlers列表转换为mPendingIdleHandlers数组
				//我猜测作用可能是提高迭代效率。
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }
			// 迭代执行所有IdleHandler
            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);
                }
				//返回false时删除IdleHandler
                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都是同步的,所有messagewhen排序,依次执行。而如果某些message优先级非常高,我们想让它在特定时间后尽早执行,应该怎么办呢?这时候就是同步屏障的用武之地了。
同步屏障功能的实现分为两个部分:1:插入一个同步屏障,使同步屏障之后的同步message暂停执行;2:插入异步message
首先看插入同步屏障的方式,我们插入同步屏障的方法是调用MessageQueue#postSyncBarrier方法:

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) {
        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) { // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
        return token;
    }
}
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;
	     }
	     if (p == null) { //没有找到token对应的同步屏障,抛出异常
	         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 the loop is quitting then it is already awake.
	     // We can assume mPtr != 0 when mQuitting is false.
	     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) { //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()); //isAsynchronous()为true即是异步信息
	 }
	 if (msg != null) {
	     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.
	         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 {
        // No more messages.
         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); /*non-static for callbacks*/
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的内容就到此为止了。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-16 11:25:31  更:2021-07-16 11:26:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/5 6:26:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码