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三大流程。

相关推荐
m0_548514773 小时前
2024.12.10——攻防世界Web_php_include
android·前端·php
凤邪摩羯3 小时前
Android-性能优化-03-启动优化-启动耗时
android
凤邪摩羯3 小时前
Android-性能优化-02-内存优化-LeakCanary原理解析
android
喀什酱豆腐4 小时前
Handle
android
m0_748232925 小时前
Android Https和WebView
android·网络协议·https
m0_748251726 小时前
Android webview 打开本地H5项目(Cocos游戏以及Unity游戏)
android·游戏·unity
m0_748254668 小时前
go官方日志库带色彩格式化
android·开发语言·golang
zhangphil8 小时前
Android使用PorterDuffXfermode模式PorterDuff.Mode.SRC_OUT橡皮擦实现“刮刮乐”效果,Kotlin(2)
android·kotlin
爱学测试的李木子8 小时前
从0到1搭建 Android 自动化 python+appium 环境
android·软件测试·python·测试工具·自动化
咸芝麻鱼8 小时前
Android Studio | 连接手机设备后,启动App时出现:Waiting For DebuggerApplication (App名)...
android·adb·智能手机·android studio