Android中同步屏障的应用及简析

同步屏障的应用

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true
            //通过postSyncBarrier()设置同步屏障,
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //handler发送消息,有了同步屏障mTraversalRunnable就会被优先执行。
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

ViewRootImpl.scheduleTraversals()里发送了Handler消息,最终会执行TraversalRunnable的run(),在这个run()中会执行doTraversal(),最终会触发View的绘制流程:measure(),layout(),draw()。为了让绘制流程尽快被执行,用到了同步屏障技术。

源码分析

开启同步屏障是通过MessageQueue.postSyncBarrier():

    private int postSyncBarrier(long when) {
        synchronized (this) {
            final int token = mNextBarrierToken++;
            //构造一个Message,没有对msg.target赋值,target是Handler
            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;
                }
            }
           //将该message插入表头
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

这里构造了一个Message,并且没有指定msg.target,最后将这个Message插入表头。这个Message就构成了一个内存屏障。

再看它是如何构成内存屏障的。根据Handler的使用及调用流程源码分析可知,取消息调用的是MessageQueue.next():

    Message next() {
        for (;;) {
            synchronized (this) {
                Message prevMsg = null;
                Message msg = mMessages;
                //根据msg.target是否为null判断内存屏障是否存在。
                if (msg != null && msg.target == null) {
                 
                    do {
                      //找出异步消息
                        prevMsg = msg;
                        msg = msg.next;
                     //根据isAsynchronous判断是否是异步消息,是异步消息则跳出循环
                    } while (msg != null && !msg.isAsynchronous());
                }
                                        //将消息返回
                    return msg;
                } 

    }

在next()中如果设置了同步屏障,那么就会通过do..while()循环优秀去找消息列表中的异步消息,找到后返回。

所以所有的异步消息都处理完后,才会处理同步消息。同步屏障就是添加了一个标识,这个标识是一个没有target的Message。如果有这个标识,就先去处理异步消息。

再看异步消息如何发送,发送消息会调到Handler.enqueueMessage():

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        //mAsynchronous只能通过Handler构造设置
        if (mAsynchronous) {
            //标记为异步消息
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

根据mAsynchronous判断是否需要标记为异步消息,这个mAsynchronous变量可以在Handler的构造中设置,一旦设置了以后,该Handler发送的所有消息都是异步消息,不能修改。

如果需要同步异步消息都发送,可以通过构造普通Handler,然后发送消息时设置msg.setAsynchronous(true)将消息标记为异步消息。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容