View体系#屏幕的刷新机制

一、刷新机制图解

1、图一

请添加图片描述

2、图二

请添加图片描述

3、图三

请添加图片描述

4、图四

请添加图片描述

五、图五

请添加图片描述

二、常见问题

1、丢帧一般是什么原因引起的

主线程有耗时操作,耽误了view的绘制。

2、安卓刷新频率60帧/秒,每隔16ms调用一次onDraw绘制一次?

刷新频率是vsync信号发生的频率。不一定每次发生vsync时都去绘制的,需要应用端主动发起requestLayout。这样才会向surfaceFlinger请求接受vsync信号。当下次信号来临时才会真正绘制。

3、onDraw之后屏幕会马上刷新吗?

不会立即刷新,要等到下次vsync信号来临时才刷新。

4、如果界面没有重绘。还会每隔16ms刷新屏幕吗?

没重绘,应用都不会收到vsync信号,但是屏幕还会每隔16ms进行次刷新,画面数据一直使用的旧的,所以看起来画面没变。

5、如果在屏幕快要刷新时才去onDraw绘制会丢帧吗?

代码里发起的绘制操作是不会立即执行的,都是下次VSync信号来临时才开始绘制的,所以什么时候发起的重绘操作对丢帧没有太大的关系。

三、scheduleTraversals的引入

1、ViewRootImpl #setView回顾
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
        
    //mWindowSession:
    //(1)在ViewRootImpl 构造中进行的初始化.
    //(2)它是一个服务端Binder代理对象,用于进行进程间通信,
    //IWindowSession是Client端的代理对象,它的Server端的实现为Session类
    final IWindowSession mWindowSession;

   //构造
    public ViewRootImpl(Context context, Display display) {
           //获取WindowSession对象,赋值给成员变量mWindowSession 
           mWindowSession = WindowManagerGlobal.getWindowSession();
           ...
    }

     public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
         ...

          int res;
           // Schedule the first layout -before- adding to the window
           // manager, to make sure we do the relayout before receiving
           // any other events from the system.
           //1、view三大流程的起点(layout、measure、draw)
           requestLayout();
           
           //2、调用mWindowSession的addToDisplay进行向WMS申请系统Window、Surface。
           res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);

     ...

   }


   @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            //核心代码: 这里是View三大流程的开始。
            scheduleTraversals(); 
        }
    }

}

这里有个重要的方法requestLayout,requestLayout内部调用了scheduleTraversals。scheduleTraversals就是View三大流程的开端。Choreographer 的相关逻辑也是在这个方法内部后续触发的。

2、Window#updateView

Window#updateView最终也会调用到scheduleTraversals。这里简略回顾下.

Window 的更新过程和Window 的添加过程是类似的。需要调用ViewManager的updateViewLayout 方法,updateViewLayout 方法在WindowManagerImpl中实现,WindowManagerImpl的updateViewLayout方法会调用WindowManagerGlobal的updateViewLayout方法

public final class WindowManagerGlobal {

    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        view.setLayoutParams(wparams);

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            // 调用setLayoutParams方法
            root.setLayoutParams(wparams, false); 
        }
}

    void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
   ...
            mWindowAttributesChanged = true;
            scheduleTraversals();
}
}
3、View#requestLayout

其实View#requestLayout最终也是会调用到ViewRootImpl#scheduleTraversals方法的

View.java

    /**
     * The parent this view is attached to.
     * {@hide}
     *
     * @see #getParent()
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
    // 留意这里的ViewParent 
    protected ViewParent mParent;
    
public void requestLayout() {
    mPrivateFlags |= PFLAG_FORCE_LAYOUT;
    mPrivateFlags |= PFLAG_INVALIDATED;

    if (mParent != null && !mParent.isLayoutRequested()) {
        //这里的mParent就是最终的容器ViewRootImpl 
        mParent.requestLayout();
    }
}
// 注意这里ViewRootImpl 为ViewParent的实现类。
// 其实
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {

}

从activity的setContentView中可以看出,activity的根view是DecorView,setContentView的view也是add到id为ID_ANDROID_CONTENT的FrameLayout下。

在ActivityThread中启动activity时,会调用handleResumeActivity方法,将DecorView 添加到ViewRootImpl中,DecorView的父类是ViewRootImpl。

4、总结

查看源码发现,当我们使用 ValueAnimator.start()、View.invalidate()时,最后也是走到ViewRootImpl的scheduleTraversals()方法。

View.invalidate()内部会循环获取ViewParent直到ViewRootImpl的invalidateChildInParent()方法,然后走到scheduleTraversals(),可自行查看源码

可知:所有UI的变化都是走到ViewRootImpl的scheduleTraversals()方法。

四、Choreographer

[ˌkɒri’ɒɡrəfə®] 舞蹈指导;编舞者

1、概览
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
        
    public boolean mTraversalScheduled;    
    int mTraversalBarrier;  
    Choreographer mChoreographer; 
    
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); 

    public ViewRootImpl(Context context, Display display) {
     mChoreographer = Choreographer.getInstance();
     ...

    }
    ...
    //ViewRootImpl#setView#requestLayout等UI相关的绘制都会走到这里。
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;//改变flag状态
            //1、获取同步屏障
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //2、注册个mTraversalRunnable回调,这个回调将在下一帧被渲染时执行。
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

    void doTraversal() {
        if (mTraversalScheduled) {
           // 绘制之前重置状态、移除同步屏障
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
            
            //View 三大流程的开始:
            //measure
            //layout
            //draw
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }


    private void performTraversals() {
    ...
    relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
    ...

    if (!mStopped || mReportNextDraw) {
    
       int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
       int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
       //view#measure
       performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
       ...
    }

     if (didLayout) {
      //view#layout
       performLayout(lp, mWidth, mHeight);
       ...
    }   

      if (!cancelDraw) {
            if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                for (int i = 0; i < mPendingTransitions.size(); ++i) {
                    mPendingTransitions.get(i).startChangingAnimations();
                }
                mPendingTransitions.clear();
            }
            //view#draw
            performDraw();
        } else {
            if (isViewVisible) {
                // Try again
                scheduleTraversals();
            } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                for (int i = 0; i < mPendingTransitions.size(); ++i) {
                    mPendingTransitions.get(i).endChangingAnimations();
                }
                mPendingTransitions.clear();
            }
        }

 
}

主要逻辑如下:
1、首先使用mTraversalScheduled字段保证同时间多次更改只会刷新一次,例如TextView连续两次setText(),也只会走一次绘制流程。

注意:这里的同一时间是指每个vsync信号周期内。因为触发scheduleTraversals之后就会修改mTraversalScheduled这个flag。只有Choreographer收到vsync回调执行runnable时mTraversalScheduled这个flag才会再次被修改。

2、然后把当前线程的消息队列Queue添加了同步屏障,这样就屏蔽了正常的同步消息,保证VSync到来后立即执行绘制,而不是要等前面的同步消息。后面会具体分析同步屏障和异步消息的代码逻辑。
3、调用了mChoreographer.postCallback()方法,发送一个会在下一帧执行的回调,即在下一个VSync到来时会执行TraversalRunnable–>doTraversal()–>performTraversals()–>绘制流程。

2、Choreographer重点分析
public final class Choreographer {
    //CallbackQueue数组内的5种链表类型。
    
    //输入事件,首先执行
    public static final int CALLBACK_INPUT = 0;
    //动画,第二执行
    public static final int CALLBACK_ANIMATION = 1;
    //插入更新的动画,第三执行
    public static final int CALLBACK_INSETS_ANIMATION = 2;
     //绘制,第四执行
    public static final int CALLBACK_TRAVERSAL = 3;
    //提交,最后执行,
    public static final int CALLBACK_COMMIT = 4;
    private static final int CALLBACK_LAST = CALLBACK_COMMIT;

    // 从系统属性读取“vsync” 值,默认为true。默认使用vsync信号
    private static final boolean USE_VSYNC = SystemProperties.getBoolean(
            "debug.choreographer.vsync", true);
            
   //1、 与Looper机制类似。Choreographer属于线程单例。
    private static final ThreadLocal<Choreographer> sThreadInstance = new ThreadLocal<Choreographer>() {
        @Override
        protected Choreographer initialValue() {
            Looper looper = Looper.myLooper(); // 获取当前线程Looper
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
            Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
            if (looper == Looper.getMainLooper()) {
                mMainInstance = choreographer;
            }
            return choreographer;
        }
    };
  // ThreadLocal 中取出单例
    public static Choreographer getInstance() {
        return sThreadInstance.get();
    }

  // 2、构造
    private Choreographer(Looper looper, int vsyncSource) {
        mLooper = looper;
        // 创建Handler对象绑定Looper
        mHandler = new FrameHandler(looper);
        
       // Android 4.1 USE_VSYNC 以上默认是true,表示 具备接受VSync的能力,
       //这个接受能力就是FrameDisplayEventReceiver
        mDisplayEventReceiver = USE_VSYNC
                ? new FrameDisplayEventReceiver(looper, vsyncSource)
                : null;
                
        mLastFrameTimeNanos = Long.MIN_VALUE;
        
        //计算一帧的时间,Android手机屏幕是60Hz的刷新频率,就是16ms
        mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

       //创建CallbackQueue类型的数组。数组大小为5
       //CallbackQueue 为链表类型,每个链表存相同类型的任务如:CALLBACK_XXX
        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }
        // b/68769804: For low FPS experiments.
        setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
    }
}

回头看mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)方法,注意到第一个参数是CALLBACK_TRAVERSAL,表示回调任务的类型,共有以下5种类型。

五种类型任务对应存入对应的CallbackQueue中,每当收到 VSYNC 信号时,Choreographer 将首先处理 INPUT 类型的任务,然后是 ANIMATION 类型,最后才是 TRAVERSAL 类型。

接着看postCallback源码

    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) {

        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            //dueTime = 当前时间+延迟时间
            final long dueTime = now + delayMillis;
            //获取相应类型链表,任务添加
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            //dueTime<当前时间就立即执行
            //否则延迟执行(延迟运行,最终也会走到scheduleFrameLocked())
            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
            //发送个延迟执行的异步消息
            //消息类型为callbackType链表类型
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }   

首先取对应类型的CallbackQueue添加任务,action就是mTraversalRunnable,token是null。CallbackQueue的addCallbackLocked()就是把 dueTime、action、token组装成CallbackRecord后 存入CallbackQueue的下一个节点

如果没有延迟会执行scheduleFrameLocked()方法,有延迟就会使用 mHandler发送MSG_DO_SCHEDULE_CALLBACK消息。注意到这里使用msg.setAsynchronous(true)把消息设置成异步,这是因为前面设置了同步屏障,只有异步消息才会执行。

延迟运行,最终也会走到scheduleFrameLocked(),这里就追踪看下mHandler如何处理消息的。

    private final class FrameHandler extends Handler {
        public FrameHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
               // 1、执行doFrame,即绘制过程
                case MSG_DO_FRAME:
                    doFrame(System.nanoTime(), 0);
                    break;
               //2、申请VSYNC信号,例如当前需要绘制任务时
                case MSG_DO_SCHEDULE_VSYNC:
                    doScheduleVsync();
                    break;
               //需要延迟的任务,最终还是执行上述两个事件.
                case MSG_DO_SCHEDULE_CALLBACK:
                //验证了延迟消息最终还是调用doScheduleCallback
                    doScheduleCallback(msg.arg1);
                    break;
            }
        }
    }

    void doScheduleCallback(int callbackType) {
        synchronized (mLock) {
            if (!mFrameScheduled) {
                final long now = SystemClock.uptimeMillis();
                if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
                    scheduleFrameLocked(now);
                }
            }
        }
    }

回来继续看scheduleFrameLocked

    private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            //一、4.1及其以上系统开启了VSYNC信号时。
            if (USE_VSYNC) { 
                
                //当前执行的线程,是否是mLooper所在线程?
                //(1)如果在则申请vsync信号
                //(2)如果不在则通过Handler发送消息到mLooper所在线程。
                //最后还是调用scheduleVsyncLocked方法
                if (isRunningOnLooperThreadLocked()) {
                    scheduleVsyncLocked();
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
                // 二、 如果未开启VSYNC则直接doFrame方法(4.1后默认开启)
            } 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);
            }
        }
    }

Android 4.1 之后系统默认开启 VSYNC,在 Choreographer 的构造方法会创建一个 FrameDisplayEventReceiver,scheduleVsyncLocked 方法将会通过它申请 VSYNC 信号。

isRunningOnLooperThreadLocked 方法,其内部根据 Looper 判断是否在原线程,否则发送消息到 FrameHandler。最终还是会调用 scheduleVsyncLocked 方法申请 VSYNC 信号。

接着就看 scheduleVsyncLocked 方法是如何申请 VSYNC 信号的。猜测肯定申请 VSYNC 信号后,信号到来时也是走doFrame() 方法,doFrame()后面再看。先跟进scheduleVsyncLocked()

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

mDisplayEventReceiver是Choreographer构造方法中创建,是FrameDisplayEventReceiver 的实例。 FrameDisplayEventReceiver是 DisplayEventReceiver 的子类,DisplayEventReceiver 是一个 abstract class

在 DisplayEventReceiver 的构造方法会通过 JNI 创建一个 IDisplayEventConnection 的 VSYNC 的监听者。FrameDisplayEventReceiver的scheduleVsync()就是在 DisplayEventReceiver中

    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 {
        	// 申请VSYNC中断信号,会回调onVsync方法
            nativeScheduleVsync(mReceiverPtr);
        }
    }

scheduleVsync()就是使用native方法nativeScheduleVsync()去申请VSYNC信号。这个native方法就看不了了,只需要知道VSYNC信号的接受回调是onVsync(),我们直接看onVsync(),具体实现是在FrameDisplayEventReceiver中。

    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, long physicalDisplayId, int frame) {

            long now = System.nanoTime();
            if (timestampNanos > now) {
                Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
                        + " ms in the future!  Check that graphics HAL is generating vsync "
                        + "timestamps using the correct timebase.");
                timestampNanos = now;
            }

            if (mHavePendingVsync) {
                Log.w(TAG, "Already have a pending vsync event.  There should only be "
                        + "one at a time.");
            } else {
                mHavePendingVsync = true;
            }

            mTimestampNanos = timestampNanos;
            mFrame = frame;
            //将本身作为runnable传入msg, 发消息后 会走run(),即doFrame(),
            //也是异步消息
            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);
        }
    }

onVsync()中,将接收器本身作为runnable传入异步消息msg,并使用mHandler发送msg,最终执行的就是doFrame()方法了。

onVsync()方法中只是使用mHandler发送消息到MessageQueue中,不一定是立刻执行,如MessageQueue中前面有较为耗时的操作,那么就要等完成,才会执行本次的doFrame()。

和上面猜测一样,申请VSync信号接收到后确实是走 doFrame()方法,那么就来看看Choreographer的doFrame()

    void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
            if (!mFrameScheduled) {
                return; // no work to do
            }

            ...
			// 预期执行时间
            long intendedFrameTimeNanos = frameTimeNanos;
            startNanos = System.nanoTime();
            
            // 超时时间是否超过一帧的时间.
            //因为MessageQueue虽然添加了同步屏障,
            //但是还是有正在执行的同步任务,导致doFrame延迟执行了.
            final long jitterNanos = startNanos - frameTimeNanos;
            if (jitterNanos >= mFrameIntervalNanos) {
            	// 计算掉帧数
                final long skippedFrames = jitterNanos / mFrameIntervalNanos;
                if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
                	// 掉帧超过30帧打印Log提示
                    Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                            + "The application may be doing too much work on its main thread.");
                }
                final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
                ...
                frameTimeNanos = startNanos - lastFrameOffset;
            }

            ...
            
            mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
            // Frame标志位恢复
            mFrameScheduled = false;
            // 记录最后一帧时间
            mLastFrameTimeNanos = frameTimeNanos;
        }

        try {
			// 按类型顺序 执行任务
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);

            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

接着看任务的具体执行doCallbacks 方法:

 void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
        
            final long now = System.nanoTime();
            // 根据指定的类型CallbackkQueue中查找到达执行时间的CallbackRecord
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;

            //提交任务类型
            if (callbackType == Choreographer.CALLBACK_COMMIT) {
                final long jitterNanos = now - frameTimeNanos;
                if (jitterNanos >= 2 * mFrameIntervalNanos) {
                    final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
                            + mFrameIntervalNanos;
                    if (DEBUG_JANK) {
                        Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
                                + " ms which is more than twice the frame interval of "
                                + (mFrameIntervalNanos * 0.000001f) + " ms!  "
                                + "Setting frame time to " + (lastFrameOffset * 0.000001f)
                                + " ms in the past.");
                        mDebugPrintNextFrameTimeDelta = true;
                    }
                    frameTimeNanos = now - lastFrameOffset;
                    mLastFrameTimeNanos = frameTimeNanos;
                }
            }
        }
        try {
            // 迭代执行队列所有任务
            for (CallbackRecord c = callbacks; c != null; c = c.next) {
                // 回调CallbackRecord的run,其内部回调Callback的run
                c.run(frameTimeNanos);
            }
        } finally {
            synchronized (mLock) {
                mCallbacksRunning = false;
                do {
                    final CallbackRecord next = callbacks.next;
                    //回收CallbackRecord
                    recycleCallbackLocked(callbacks);
                    callbacks = next;
                } while (callbacks != null);
            }
        }
    }

主要内容就是取对应任务类型的队列,遍历队列执行所有任务,执行任务是 CallbackRecord的 run 方法:

 private static final class CallbackRecord {
        public CallbackRecord next;
        public long dueTime;
        public Object action; // Runnable or FrameCallback
        public Object token;

        @UnsupportedAppUsage
        public void run(long frameTimeNanos) {
            if (token == FRAME_CALLBACK_TOKEN) {
            	// 通过postFrameCallback 或 postFrameCallbackDelayed,会执行这里
                ((FrameCallback)action).doFrame(frameTimeNanos);
            } else {
            	//取出Runnable执行run()
                ((Runnable)action).run();
            }
        }
    }

前面看到mChoreographer.postCallback传的token是null,所以取出action,就是Runnable,执行run(),这里的action就是 ViewRootImpl 发起的绘制任务mTraversalRunnable了,那么这样整个逻辑就闭环了。

3、问题

那么 啥时候 token == FRAME_CALLBACK_TOKEN 呢?答案是Choreographer的postFrameCallback()方法

 public void postFrameCallback(FrameCallback callback) {
        postFrameCallbackDelayed(callback, 0);
    }
    
    public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
        if (callback == null) {
            throw new IllegalArgumentException("callback must not be null");
        }

		//也是走到是postCallbackDelayedInternal,并且注意是CALLBACK_ANIMATION类型,
		//token是FRAME_CALLBACK_TOKEN,action就是FrameCallback
        postCallbackDelayedInternal(CALLBACK_ANIMATION,
                callback, FRAME_CALLBACK_TOKEN, delayMillis);
    }

    public interface FrameCallback {
        public void doFrame(long frameTimeNanos);
    }

可以看到postFrameCallback()传入的是FrameCallback实例,接口FrameCallback只有一个doFrame()方法。并且也是走到postCallbackDelayedInternal,FrameCallback实例作为action传入,token则是FRAME_CALLBACK_TOKEN,并且任务是CALLBACK_ANIMATION类型。

4、Choreographer的postFrameCallback()

Choreographer的postFrameCallback()通常用来计算丢帧情况,栗子如下:

(1)App#Application.java

         public void onCreate() {
             super.onCreate();
             //在Application中使用postFrameCallback
             Choreographer.getInstance().postFrameCallback(new FPSFrameCallback(System.nanoTime()));
         }

(2)计算类

   public class FPSFrameCallback implements Choreographer.FrameCallback {

      private static final String TAG = "FPS_TEST";
      private long mLastFrameTimeNanos = 0;
      private long mFrameIntervalNanos;

      public FPSFrameCallback(long lastFrameTimeNanos) {
          mLastFrameTimeNanos = lastFrameTimeNanos;
          mFrameIntervalNanos = (long)(1000000000 / 60.0);
      }

      @Override
      public void doFrame(long frameTimeNanos) {

          //初始化时间
          if (mLastFrameTimeNanos == 0) {
              mLastFrameTimeNanos = frameTimeNanos;
          }
          final long jitterNanos = frameTimeNanos - mLastFrameTimeNanos;
          if (jitterNanos >= mFrameIntervalNanos) {
              final long skippedFrames = jitterNanos / mFrameIntervalNanos;
              if(skippedFrames>30){
              	//丢帧30以上打印日志
                  Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                          + "The application may be doing too much work on its main thread.");
              }
          }
          mLastFrameTimeNanos=frameTimeNanos;
          //注册下一帧回调
          Choreographer.getInstance().postFrameCallback(this);
      }
  }
5、流程小结

在这里插入图片描述

The end

参考1

参考2

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值