Android绘制流程之Choreographer分析

本文解析了Android中Choreographer在Vsync协调下的UI绘制流程,重点讲解了`scheduleTraversals()`和`postCallback()`方法的工作原理,涉及帧率管理、动画处理和View的measure、layout、draw过程。
摘要由CSDN通过智能技术生成

前言

在Android的绘制流程中,会走到ViewRootImpl里的scheduleTraversals()函数

//代码文件:ViewRootImpl.java
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

在这里面我们可以看到有mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

mChoreographer就是今天我们要分析的对象Choreographer(编舞者)

Choreographer

假如把硬件设备性能完全开放,假如GPU制图速率是120FPS(一秒钟出120张图),而屏幕只有60HZ(1秒钟刷新60次),那就会造成一些问题(丢失一些帧数),所以需要Choreographer进行帧率上的管理。

Choreographer的引入,主要是配合Vsync,给上层App的渲染提供一个稳定的绘制处理时机,也就是Vsync(同步信号)到来的时候,Choreographer可以接收VSync信号,统一管理应用的输入、动画、绘制等任务的执行实际。Android的UI绘制任务将在他的同一指挥下完成,这个是引入Choreographer的作用:

  • 业内一般用它来监控应用的帧率;
  • Choreographer进行帧率的管理;
  • Choreographer他来完成对于16.6ms的时间管理判定以及跳帧处理;
  • Choreographer的定位就是去控制,什么时候请求同步信息且推动绘制启动;

ok,看完Choreographer的介绍后,我们回到上述代码的位置mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); 我们往下跟随代码,看看Choreographer是怎么去跟Vsync(同步信号)配合,进而绘制界面的。

//代码文件:Choreographer.java

public void postCallback(int callbackType, Runnable action, Object token) {
    postCallbackDelayed(callbackType, action, token, 0);
}

public void postCallbackDelayed(int callbackType,
        Runnable action, Object token, long delayMillis) {
    if (action == null) {
        throw new IllegalArgumentException("action must not be null");
    }
    if (callbackType < 0 || callbackType > CALLBACK_LAST) {
        throw new IllegalArgumentException("callbackType is invalid");
    }

    postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}

private void postCallbackDelayedInternal(int callbackType,
        Object action, Object token, long delayMillis) {
    if (DEBUG_FRAMES) {
        Log.d(TAG, "PostCallback: type=" + callbackType
                + ", action=" + action + ", token=" + token
                + ", delayMillis=" + 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);
        }
    }
}

最终会调用到postCallbackDelayedInternal方法,首先先把action存进mCallbackQueues里,这里的action就是后续我们要调用到的measurelayoutdraw;接着通过判断dueTime,要么执行scheduleFrameLocked(now);要么通过mHandler发送一个Message,最终还是会执行到scheduleFrameLocked(now);

//代码文件:Choreographer.java

private void scheduleFrameLocked(long now) {
    if (!mFrameScheduled) {
        mFrameScheduled = true;
        if (USE_VSYNC) {
            if (DEBUG_FRAMES) {
                Log.d(TAG, "Scheduling next frame on vsync.");
            }

            // If running on the Looper thread, then schedule the vsync immediately,
            // otherwise post a message to schedule the vsync from the UI thread
            // as soon as possible.
            if (isRunningOnLooperThreadLocked()) {
                scheduleVsyncLocked();
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtFrontOfQueue(msg);
            }
        } else {
            final long nextFrameTime = Math.max(
                    mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
            if (DEBUG_FRAMES) {
                Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
            }
            Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, nextFrameTime);
        }
    }
}

isRunningOnLooperThreadLocked()判断如果是在主线程上,则向底层请求同步信号,如果是子线程的绘制消息则通过消息发送,两者最终都是走到scheduleVsyncLocked();

//代码文件:Choreographer.java

private final FrameDisplayEventReceiver mDisplayEventReceiver;

private void scheduleVsyncLocked() {
    mDisplayEventReceiver.scheduleVsync();
}

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
        implements Runnable {
    private boolean mHavePendingVsync;
    private long mTimestampNanos;
    private int mFrame;

    public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
        super(looper, vsyncSource);
    }

    ......
}

这里调用到mDisplayEventReceiver.scheduleVsync();,我们需要到mDisplayEventReceiver的父类看它的scheduleVsync()具体实现。

//代码文件:DisplayEventReceiver.java

public void scheduleVsync() {
    if (mReceiverPtr == 0) {
        Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                + "receiver has already been disposed.");
    } else {
        //走这里
        nativeScheduleVsync(mReceiverPtr);
    }
}

最终调用到nativeScheduleVsync(mReceiverPtr);,是一个native调用,主要作用是去底层找SurfaceFlinger请求vSync同步信号

等底层垂直同步信号发送过来后,会回调dispatchVsync方法:

//代码文件:DisplayEventReceiver.java

// Called from native code.
@SuppressWarnings("unused")
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
    onVsync(timestampNanos, builtInDisplayId, frame);
}


进而调用到子类FrameDisplayEventReceiveronVsync方法:

//代码文件:Choreographer.java

private final FrameDisplayEventReceiver mDisplayEventReceiver;

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
        implements Runnable {
    private boolean mHavePendingVsync;
    private long mTimestampNanos;
    private int mFrame;

    public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
        super(looper, vsyncSource);
    }

    @Override
    public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
      
       ......

        mTimestampNanos = timestampNanos;
        mFrame = frame;
        Message msg = Message.obtain(mHandler, this);
        msg.setAsynchronous(true);
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }

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

可以看到创建了一个Message,并且在初始化Message时,给callback赋值了,所以后续会执行到run()方法,进而执行到doFrame()方法。

doFrame()函数中执行了应用层的callback,基本上包含了一帧的渲染工作:

//代码文件:oreographer.java

void doFrame(long frameTimeNanos, int frame) {
    //自带了掉帧计算
    if (jitterNanos >= mFrameIntervalNanos) {
        final long skippedFrames = jitterNanos / mFrameIntervalNanos;
        if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
            Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                    + "The application may be doing too much work on its main thread.");
        }
    }
    doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
}



其中CALLBACK_ANIMATION是处理动画相关的逻辑,而CALLBACK_TRAVERSAL则会调用到ViewRootImplperformTraversals() 函数,从而执行到我们所熟悉的View的measurelayoutdraw三大流程。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题
图片

  • 35
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值