Android Handler(2)同步屏障分析

82 阅读3分钟

# Android Handler(1)流程分析

在之前的文章中,looper.loop方法中,取消时候的时候调用了messagequeue.next()。

image.png 其中我们可以看到,msg不为null,并且msg.target为null。这种情况,handler不就是为null了。那么还怎么处理消息。 往下看,红框里有一个判断,它取出了一条消息。第二个判断条件是message的方法。看一下。

image.png 注释是,如果是异步消息就会返回true,那么同步消息就会返回false。所以上面的代码的意思就是一直找消息,知道找到一条异步消息就跳出循环。

既然我们已经知道这一步的操作是为了取出一个异步消息,那么收钱从头来看,target为什么为null,走入了这个判断。

1.首先message中与asyn相关的就两个,(获取同步异步和设置同步异步,与target不相关)

2.在看handler,handler方法最后都会把自己作为this传入

3.那么接下来就要从messagequeue来查看了。可以看到这个方法

同步屏障(所谓的)

image.png 我把注释也贴了上去。看注释就懂了。向消息队列中发布一个同步屏障,当遇到障碍的时候,列队中的同步消息会被阻止执行。直到找到一个异步消息。

从红框内,我们也可以看到,msg.target并没有被赋值,由此可见,会走到我们开头图片的方法中。

那我们就来说说消息的类型(同步消息、异步消息、同步屏障消息)

什么是同步屏障?

简单的来说就是一个target为null的消息,他的作用就是当代码发现这个同步屏障后,阻止同步消息的读取,去找队列中的异步消息。

什么是同步消息和异步消息?

//可以看到同步消息和异步消息的区别,仅仅就是一个flag的值的不同而已。
public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
 }

同步屏障用来做什么?

流程如果所示(其实就是取异步消息):

同步屏障.drawio.png

那么我们此时就知道流程了。

但是 postSyncBarrier 这个方法我们貌似一直没有用过。可以看到方法上有一个注解。@UnsupportedAppUsage,我理解的意思就是不支持app去使用。????那是谁用的。我们源码中搜索一下。

image.png 看到了一个老朋友。

这里不提及太多,以后有屏幕刷新的文章在单独介绍。简单的说就是屏幕刷新一般为60hz~120hz,每秒刷新60到120次,对用来说体验是非常重要的,所以肯定要保证系统的刷新率优先,所以同步屏障就可以解决这个问题。当然说起屏幕刷新也涉及到vsync同步信号等,以后有时间更一篇屏幕刷新的文章。

同步屏障用在哪里?

ViewRootImpl.java(device中也用到,这里不说明了。)

调用requestLayout或者invalidate时,会在主线程消息队列中插入一个同步屏障消息,
同时注册接收Vsync的监听;当接受到Vsync通知,会发送一个异步消息,执行真正的绘制事件:
此时会移除消息队列中的同步屏障消息,然后才会执行绘制操作
  @UnsupportedAppUsage
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

    void unscheduleTraversals() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            mChoreographer.removeCallbacks(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

最后,我们知道同步屏障是什么,用来做什么,就可以了。有兴趣可以多看看源码,研究下。