Handler同步屏障
Posted guangdeshishe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Handler同步屏障相关的知识,希望对你有一定的参考价值。
Handler同步屏障功能小结
- 当
MessageQueue.postSyncBarrier
发送了一个同步屏障消息后,所有的同步消息(普通Message)都不能被执行,只有被标记为异步的Message才能执行; MessageQueue.removeSyncBarrier
移除屏障消息后,普通消息才能正常继续执行;- 正常的消息
Message.target
指向Handler,并且Message默认是同步消息,如果想变成异步消息,可以调用Message.setAsynchronous(true)
方法,或者Handler初始化的时候public Handler(boolean async)
传入true
,之后通过该Handler发送的消息就都是异步消息了 - 发送同步屏障本质上只是往
MessageQueue
中插入一个Message.target=null
的消息 MessageQueue.next
方法中检测到有同步屏障消息时,则会开启一个while
循环过滤出异步消息继续执行
源码分析
-
MessageQueue.postSyncBarrier
该API不能被APP正常调用,也没有这个必要,因为APP想要不受同步屏障的影响,可以将Message设置为异步消息即可@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) final int token = mNextBarrierToken++; final Message msg = Message.obtain();//创建一个Message,但是没有设置target 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
方法中,根据msg.target == null
来判断是同步屏障消息,从而开始while循环只处理异步消息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());
-
Handler初始化时可以指定发送的消息是同步的还是异步的
public Handler(boolean async) this(null, async); public Handler(@Nullable Callback callback, boolean async) mAsynchronous = async; private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) //标记为异步消息 msg.setAsynchronous(true); return queue.enqueueMessage(msg, uptimeMillis);
-
ViewRootImpl.java
中应用同步屏障功能;事件分发消息不受影响,因为设置成了异步消息void scheduleTraversals() if (!mTraversalScheduled) mTraversalScheduled = true; //执行同步前发送同步屏障消息 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); //请求并等待垂直同步信号VSync mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); void doTraversal() if (mTraversalScheduled) mTraversalScheduled = false; //移除垂直同步屏障 mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) Debug.startMethodTracing("ViewAncestor"); //开始真正绘制 performTraversals(); if (mProfile) Debug.stopMethodTracing(); mProfile = false; public void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) SomeArgs args = SomeArgs.obtain(); args.arg1 = event; args.arg2 = receiver; Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, args); msg.setAsynchronous(true);//事件分发消息不受影响,因为设置为异步消息 mHandler.sendMessage(msg);
应用场景
ViewRootImpl.java
中,绘制流程开始之前,会通过主线程的MessageQueue
发送一个同步消息屏障,然后开始等待垂直同步信号VSync,等待垂直同步信号这段时间内,所有在主线程Handler发送的同步消息都不能被及时执行,这样做可以保证垂直同步信号到来时可以及时的执行绘制流程,保证UI的流畅性- 为了保证我们主线程Handler消息能及时处理,可以调用
Message.setAsynchronous(true)
方法将Message设置为异步消息
以上是关于Handler同步屏障的主要内容,如果未能解决你的问题,请参考以下文章
深入源码分析Handler 消息机制 LooperMessageQueue 消息同步屏障IdleHandlerMessage 复用