【面向面试学习】Handler的同步屏障机制

103 阅读3分钟

精简版回答

同步屏障就是屏蔽同步消息的同时允许异步消息通过

详细版回答

同步屏障(SyncBarrier,翻译成同步栅栏也可以)

Message分为同步和异步

平时我们创建的Message默认都是同步类型的,有些时候需要创建异步Message 通过设置setAsynchronous(true)将Message指定为异步

同步屏障如何定义

同步屏障就是插入到Handler里MessageQueeen里的特殊Message

同步屏障如何实现

既然是个特殊的Message,那么特殊在哪? 这个Message特殊在target是的,这样的Message就是具体担当同步屏障角色的特殊Message,也就是栅栏本体。 众所周知MessageQueeen总是在不停的遍历消息队列,当它发现targe为空的Message时,就认为发现一个同步屏障,然后继续遍历,寻找isAsynchronous的消息,也就是寻找消息类型为异步的Message,找到后执行这个消息,而普通的同步消息则得不到执行,实现屏障拦截的效果,除非将这个target为空的Message从MessageQueeen移除,普通的同步消息才能得到执行。

Handler.java 遍历时发现同步屏障

Message next() {

   if (msg != null && msg.target == null) {//发现同步屏障
       do {
            prevMsg = msg;
             msg = msg.next;
          } while (msg != null && !msg.isAsynchronous());//直到找到异步消息为止
     }

如何给MessageQueeen插入一个同步屏障

Handler.java 里有个私有方法,这就是给MessageQueeen插入一个同步屏障,返回值是一个同步屏障toekn,这个token用于删除一个同步屏障时使用。

private int postSyncBarrier(long when) {}//添加一个同步屏障

public void removeSyncBarrier(int token){}//删除一个同步屏障

插入一个同步屏障

Android的UI绘制时需要ViewRootImpl参与其中

ViewRootImpl.java

在调用requestLayout请求布局时会触发scheduleTraversals

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //这里这里这里!插入一个同步屏障
        //mHandler是Choreographer里定义的FrameHandler的实例
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //这里注册的Callback回调,会被Vsync信号触发,也就是当硬件发起垂直同步信号后,这里的回调会被触发执行绘制动作,由于Vsync的优先级很高,所以开启同步屏障保障Vsync相关的操作优先独占的得到执行
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//关注这个mTraversalRunnable
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

创建异步消息

Choreographer.java

看下postCallback的实现,同步屏障就是屏蔽同步消息,保障异步消息的执行,异步消息就是这里创建,并插入MessageQueeen的

private void postCallbackDelayedInternal(int callbackType,
        Object action, Object token, long delayMillis) {
    synchronized (mLock) {
        final long now = SystemClock.uptimeMillis();
        final long dueTime = now + delayMillis;
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime <= now) {
            scheduleFrameLocked(now);
        } else {
            ///上面看到的mTraversalRunnable被传进来,成为这里的action
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            msg.setAsynchronous(true);//异步消息诞生了!!!!!!!
            mHandler.sendMessageAtTime(msg, dueTime);
            //mHandler是Choreographer里定义的FrameHandler的实例
        }
    }
}

截至现在 同步屏障也有了异步Message也有了,MessageQueen的工作状态进入同步屏障模式,在next()中逐步消费这些Message

移除同步屏障

回到ViewRootImpl.java


//mTraversalRunnable就是下面这个类的实例,异步Message执行时会回调这里 
final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}

//移除刚才添加的同步屏障后执行performTraversals()去具体的绘制UI
void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);//移除同步屏障

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }

        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}

总结

  1. 插入同步屏障
  2. 插入异步消息
  3. 消费异步消息
  4. 移除同步屏障
  5. 同步屏障是系统私有方法,对普通开发者不开放,除非反射