Android绘制流程之Choreographer分析

前言

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

java 复制代码
//代码文件: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(同步信号)配合,进而绘制界面的。

java 复制代码
//代码文件: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);

java 复制代码
//代码文件: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();

java 复制代码
//代码文件: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()具体实现。

java 复制代码
//代码文件: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同步信号(具体过程参考:blog.csdn.net/j383575602/...

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

java 复制代码
//代码文件:DisplayEventReceiver.java

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

进而调用到子类FrameDisplayEventReceiveronVsync方法:

java 复制代码
//代码文件: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,基本上包含了一帧的渲染工作:

java 复制代码
//代码文件: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三大流程。

相关推荐
雨白4 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹5 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空7 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭7 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日8 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安8 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑8 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟13 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡14 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0014 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体