Choreographer初识(2)——View的UI重绘

689 阅读2分钟

「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

view的重绘过程首先会调用requestLayout方法。

屏幕的重绘示意图

2021-11-13 18-02-59屏幕截图.png

底层刷新机制存在的缺陷。

Choreographer引入对这个进行了优化,只有在Vsync发送前完成view的重绘屏幕才会进行刷新。

Choreographer发一个消息等下一个Vsync信号来的时候去处理消息,使得VIew的重绘发生在Vsync信号到来后就立刻执行。

2021-11-15 13-47-24屏幕截图.png

requestLayout

发起UI重绘

/frameworks/base/core/java/android/view/ViewRootImpl.java$requestLayout

@Override
public void requestLayout() {
    //...
        scheduleTraversals();
    //..
}
void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            //postSyncBarrier加入同步屏障;同步屏障其实就是一个handler为空的消息,它也存放在消息队列中
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

没有直接绘制,做了两件事,

一个是往线程消息队列插入了一个SyncBarrier。其实是一个屏障,屏障后边的普通消息不能去处理,对异步消息没有影响。(有些消息比较紧急。)

另一个是向Choreographer的消息队列中插入了一个CALLBACK_TRAVERSAL,mTraversalRunnable等下一个Vsync信号来的时候要紧急处理这个消息。

mChoreographer的初始化创建在之前已经介绍过了。

mTraversalScheduled变量控制了重绘,在Vsync周期里面只会出现一次重绘。mTraversalRunnable进行重绘,当下一个Vsync信号来的时候才回去执行,将mTraversalScheduled设置为false,进而才可以接着requestLayout。

void doTraversal() {。
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            //...
            //进行真正的重绘
            performTraversals();
            //...
        }
    }

紧接着我们去看callback是如何加到Choreographer中的。

private void postCallbackDelayedInternal(int callbackType,
                                         Object action, Object token, long delayMillis) {
    
    //根据callback类型插入到对应的单列表,根据duetime时间顺序去排序。
    mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
​
    if (dueTime <= now) {
        scheduleFrameLocked(now);
    } else {
        Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
        msg.arg1 = callbackType;
        msg.setAsynchronous(true);
        mHandler.sendMessageAtTime(msg, dueTime);
    }
}
}

mCallbackQueues数组中的每个类型都是一个callback单链表,根据callback类型插入到对应的单列表,根据callback的执行时间顺序去排序。

 private void scheduleFrameLocked(long now) {
     //...
     //如果就是Choreographer的工作线程
     if (isRunningOnLooperThreadLocked()) {
         scheduleVsyncLocked();
     } else {
         //如果不是会发一个消息到Choreographer的工作线程
         Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
         msg.setAsynchronous(true);//异步消息不受屏障影响
         //消息插入消息队列头部,告诉SurfaceFlinger在下一个Vsync信号来的时候进行通知
         mHandler.sendMessageAtFrontOfQueue(msg);
     }
     //...
 }
​

如果就是Choreographer的工作线程就直接执行scheduleVsyncLocked方法,如果不是的话,会发送一个消息到Choreographer的工作线程,而且发送的是一个异步消息不受屏障的影响。并且将消息插入消息队列头部,告诉SurfaceFlinger在下一个Vsync信号来的时候去通知我们。去请求这个Vsync信号,当拿到这个信号后有onVsync去响应,下篇文章继续去分析。