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 小时前
安卓APP如何适配不同的手机分辨率
android·智能手机
xvch4 小时前
Kotlin 2.1.0 入门教程(五)
android·kotlin
xvch8 小时前
Kotlin 2.1.0 入门教程(七)
android·kotlin
望风的懒蜗牛8 小时前
编译Android平台使用的FFmpeg库
android
浩宇软件开发9 小时前
Android开发,待办事项提醒App的设计与实现(个人中心页)
android·android studio·android开发
ac-er88889 小时前
Yii框架中的多语言支持:如何实现国际化
android·开发语言·php
苏金标10 小时前
The maximum compatible Gradle JVM version is 17.
android
zhangphil10 小时前
Android BitmapShader简洁实现马赛克,Kotlin(一)
android·kotlin
iofomo15 小时前
Android平台从上到下,无需ROOT/解锁/刷机,应用级拦截框架的最后一环,SVC系统调用拦截。
android
我叫特踏实16 小时前
SensorManager开发参考
android·sensormanager