MessageQueue之SyncBarrier的作用

mac2025-06-21  11

SyncBarrier作用是保证队列中的异步类型msg的优先执行,提高特殊msg的优先级。

一、postSyncBarrier()方法

该方法是hide的,业务不能直接使用,可以通过反射方式进行调用。调用该方法,会在消息队列中插入一个空msg。与该方法对应的public void removeSyncBarrier(int token) ;方法是删除token对应的msg。

private int postSyncBarrier(long when) { // 保证多线程的正确性,加锁 synchronized (this) { // 返回一个int型值,一个自增的成员变量 final int token = mNextBarrierToken++; // 获取一个空msg,target==null,插入到队列中 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; } }

二、MessageQueue的next()方法

设置的屏障是在出队的时候使用的,在出队时,判断队头消息是否是空消息(说明设置了SyncBarrier),如果是,则按顺序执行队列后面的异步消息,直到SyncBarrier被remove,才会执行队列中的同步消息。

Message next() { final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; // msg.target == null 说明当前队头是一个空消息,被设置了SyncBarrier if (msg != null && msg.target == null) { // 找到该消息后面的异步消息(msg.isAsynchronous()方法) 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; // prevMsg不为空,从队列中删除即将执行的msg,但是队头保持不变,即在下一个循环中,队头还是个空消息 // 从这里可以看出,如果被设置了SyncBarrier,只有当SyncBarrier被移除的时候,才会执行队列后面的非异步消息 if (prevMsg != null) { prevMsg.next = msg.next; } else { // 替换队头 mMessages = msg.next; } msg.next = null; msg.markInUse(); return msg; } } else { // 队列中没有可用消息 nextPollTimeoutMillis = -1; } } } }

三、SyncBarrier在UI刷新中的使用

1、UI有刷新需要时,首先会执行ViewRootImpl的scheduleTraversals()方法,接收vsync信号也是通过handler机制(且msg是异步的,在Choreographer类中),所以此处设置了一个屏障,会使从当前时间点,到接收到vsync信号这段时间所有被插入到消息队列中的同步消息失效,直到接收到vsync信号后,把屏障移除。这样就保证了UI刷新信号的高优先级,保证了UI刷新的及时性。

void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; // 设置屏障 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); // 下一个vsync信号会回调mTraversalRunnable mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } } // 该方法在接收到vsync信号时调用,执行view的measure、layout和draw void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; // 移除屏障 mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); performTraversals(); } }
最新回复(0)