从postCallback()说起,Choreographer在视图渲染中扮演什么角色?

3,045 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第29天,点击查看活动详情

接下来会出三篇文章详细介绍,View视图如何经过一遍遍的流程渲染到界面上的,从ActivityonResume(),经过Choreographer处理,经过FrameDisplayEventReceiver处理,最终触发View的measure、layout、draw三大流程,将视图绘制到界面上。

历史文章

从onResume()分析,ViewRootImpl在视图渲染中扮演什么角色?

概述

上篇文章讲了,ViewRootImpl类负责将视图通过WMS添加到真正的显示窗口中,并通过方法Choreographer.postCallback()方法添加渲染回调,触发后续的View的三大流程measure、layout、draw。

本篇文章就带你了解下Choreographer.postCallback()做了什么,如何触发后续View的三大渲染流程的。

postCallback()方法开始分析

image.png

这个传入的mTraversalRunnable即Runnable对象是啥呢,看下:

image.png

关键的方法就是doTraversal(),这个方法最终会触发我们最终的View测量布局绘制流程,这个会放到另一篇文章中进行分析。

我们先一步步看下调用链:

image.png image.png

最终调用到目标方法postCallbackDelayedInternal()

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 {
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}
  1. 首先将外部传入的Runnable即action对象设置到mCallbackQueues这个数组中,记住这个数组,后面会有大用;

  2. delayMillis这个参数值传入的为0,所以判断条件dueTime <= now成立,会走到方法scheduleFrameLocked()中:

private void scheduleFrameLocked(long now) {
    if (!mFrameScheduled) {
        mFrameScheduled = true;
        if (USE_VSYNC) {
            if (isRunningOnLooperThreadLocked()) {
                scheduleVsyncLocked();
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtFrontOfQueue(msg);
            }
        } 
    }
}

我们看下这段代码逻辑,USE_VSYNC标识是否监听使用垂直脉冲信号,Android5.0之后这个值都是为true的;

另外isRunningOnLooperThreadLocked()方法就是判断当前线程是否和创建ViewRootImpl的线程是同一个,如果不是就通过Handler切换到创建线程中执行。最终都会调用到方法scheduleVsyncLocked()

image.png

mDisplayEventReceiver是一个FrameDisplayEventReceiver类型对象,其中FrameDisplayEventReceiver继承了抽象类DisplayEventReceiver,所以最终这个方法scheduleVsync()会走到DisplayEventReceiver类中 :

image.png image.png

nativeScheduleVsync()是一个native方法,这个方法最终会注册接收底层的垂直脉冲信号的,一般为16ms发送一次。

既然注册了,那监听到后的回调方法肯定也是有的,那就是DisplayEventReceiver.onVsync()方法:

image.png

可以看到这个方法的注释中也是有写到,当底层的垂直脉冲信号到来时,这个方法就会被调用

FrameDisplayEventReceiver子类重写了onVsync()方法:

image.png

关键的代码就是在红框中,发送一个异步消息(提高渲染相关任务执行的优先级),并将当前的FrameDisplayEventReceiver对象作为Runnable对象传入到Handler中分发执行。

FrameDisplayEventReceiver实现了Runnable接口,我们看下其实现的run方法:

public void run() {
    mHavePendingVsync = false;
    doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
}

doFrame()方法就是非常关键的一个核心方法了,这个方法就会从上面添加的数组mCallbackQueues中取出视图渲染的回调开始执行,具体的详细执行逻辑我们放到下一篇也是最后一篇文章进行分析

总结

本篇文章分析了Choreographer在视图渲染中扮演的角色:

  1. 添加视图渲染回调到数组中;

  2. 注册接收底层垂直脉冲信号;

  3. 接受脉冲信号触发java层开始视图的真正渲染;

希望本篇文章能给你带来帮助,感谢阅读!!