APM框架Matrix源码分析(五)Choreographer源码分析

139 阅读8分钟

Choreographer顾名思义编舞者,用来接收硬件发送的Vsync (垂直同步)信号。Android系统每隔16ms会发出Vsync信号,来通知重绘、渲染,每一帧渲染会回调doFrame(frameTimeNanos: Long)

Choreographer通常用来计算掉帧情况

 fun start() {
       Choreographer.getInstance().postFrameCallback(object : Choreographer.FrameCallback {
           var lastFrameTimeNanos: Long = 0
           override fun doFrame(frameTimeNanos: Long) {
               //上次回调时间
               if (lastFrameTimeNanos == 0L) {
                   lastFrameTimeNanos = frameTimeNanos
                   //注册下一帧回调
                   Choreographer.getInstance().postFrameCallback(this)
                   return
              }
               val diff = (frameTimeNanos - lastFrameTimeNanos) / 1000000
               if (diff > 16.6f) {
                   //掉帧数
                   val droppedFrameCount = (diff / 16.6).toInt()
              }
               lastFrameTimeNanos = frameTimeNanos
               //注册下一帧回调
               Choreographer.getInstance().postFrameCallback(this)
          }
      })
  }

Choreographer通过ThreadLocal实现线程单例,每个线程独有。

Choreographer初始化
/**
 * Gets the choreographer for the calling thread.  Must be called from
 * a thread that already has a {@link android.os.Looper} associated with it.
 *
 * @return The choreographer for this thread.
 * @throws IllegalStateException if the thread does not have a looper.
 */
public static Choreographer getInstance() {
    return sThreadInstance.get();
}

//Choreographer是线程单例,每个线程独有(即线程间不共享)
// Thread local storage for the choreographer.
private static final ThreadLocal<Choreographer> sThreadInstance =
  new ThreadLocal<Choreographer>() {
  @Override
  protected Choreographer initialValue() {
    //获取当前线程的Looper
    Looper looper = Looper.myLooper();
    if (looper == null) {
      throw new IllegalStateException("The current thread must have a looper!");
    }
    //创建Choreographer
    Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
    if (looper == Looper.getMainLooper()) {
      mMainInstance = choreographer;
    }
    return choreographer;
  }
};

 private Choreographer(Looper looper, int vsyncSource) {
   mLooper = looper;
   //1.初始化FrameHandler(消息处理的Handler)
   mHandler = new FrameHandler(looper);
   //2.初始化DisplayEventReceiver,用来接收Vsync信号
   mDisplayEventReceiver = USE_VSYNC
     ? new FrameDisplayEventReceiver(looper, vsyncSource)
     : null;
   //记录上一次收到Vsync信号的时间(单位:纳米)
   mLastFrameTimeNanos = Long.MIN_VALUE;
	 //定义一帧的时间,Android屏幕60HZ的刷新频率,就是16ms
   mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
	 //数组中有5个链表,每个链表存放input、animation、traversal事件的callback回调
   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));
 }

Choreographer通过ThreadLocal实现线程单例,每个线程独有。

其内部维护了一个数组mCallbackQueues,每个数组下标都是一个链表CallbackQueue(链表中的元素按照时间从小到大排序)。

    //输入事件,首先执行
    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;

五种类型的任务存入对应的链表CallbackQueue,每当收到Vsync信号时,Choreographer首先依次INPUT、ANIMATION、TRAVERSAL类型任务。

外部通过postCallback去安排任务,将任务放在对应下标链表,调用一次只会接收一次信号,接收到信号处理完逻辑循环调用。

postCallback调用了postCallbackDelayed,接着调用了postCallbackDelayedInternal

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;
        //把dueTime, action, token组装成CallbackRecord存入对应类型链表的下一个节点
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime <= now) {
            //无延迟则立即执行
            scheduleFrameLocked(now);
        } else {
            //有延迟则通过mHandler发送MSG_DO_SCHEDULE_CALLBACK消息,最终也走到scheduleFrameLocked
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            //设置异步消息,优先执行
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}

接着看下FrameHandler对MSG_DO_SCHEDULE_CALLBACK消息的处理

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

  @Override
  public void handleMessage(Message msg) {
    switch (msg.what) {
      case MSG_DO_FRAME:
        //执行doFrame(即绘制过程)
        doFrame(System.nanoTime(), 0, new DisplayEventReceiver.VsyncEventData());
        break;
      case MSG_DO_SCHEDULE_VSYNC:
        //申请Vsync信号(当前需要绘制任务时)
        doScheduleVsync();
        break;
      case MSG_DO_SCHEDULE_CALLBACK:
        //通过Handler发送的延迟任务,也走到了scheduleFrameLocked
        doScheduleCallback(msg.arg1);
        break;
    }
  }
}

延迟执行也走到了scheduleFrameLocked

private void scheduleFrameLocked(long now) {
    if (!mFrameScheduled) {
        mFrameScheduled = true;
        //4.1及以上默认开启了Vsync
        if (USE_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()) {
              	//在原线程申请Vsync信号
                scheduleVsyncLocked();
            } else {
                //不在则用mHandler发送消息到原线程,最终还是走到scheduleVsyncLocked
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtFrontOfQueue(msg);
            }
        } else {
            //未开启Vsync直接走doFrame(通过mHandler发送MSG_DO_FRAME消息)
            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);
        }
    }
}

接着看下scheduleVsyncLocked是如何申请Vsync信号的:

private final FrameDisplayEventReceiver mDisplayEventReceiver;
private void scheduleVsyncLocked() {
  	//调用父类的scheduleVsync方法
    mDisplayEventReceiver.scheduleVsync();
}

mDisplayEventReceiver是在Choreographer的构造方法中创建的,其父类DisplayEventReceiver注册了Vsync信号监听:

public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration) {
    if (looper == null) {
        throw new IllegalArgumentException("looper must not be null");
    }

    mMessageQueue = looper.getQueue();
    //通过JNI注册Vsync信号监听
    mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
            vsyncSource, eventRegistration);
}

@UnsupportedAppUsage
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 {
    //scheduleVsync使用native方法去申请Vsync信号,回调是onVsync
    nativeScheduleVsync(mReceiverPtr);
  }
}

构造DisplayEventReceiver时使用native方法注册了Vsync信号监听,native方法去申请了Vsync信号,回调到了

onVsync,具体实现在FrameDisplayEventReceiver的onVsync:

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
        implements Runnable {

    public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
        super(looper, vsyncSource, 0);
    }
  
    @Override
    public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
            VsyncEventData vsyncEventData) {
            //...
            //将自身的Runable传入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, mLastVsyncEventData);
    }
}

接收到Vsync信号回调后,使用mHandler发送异步消息到MessageQueue,执行run方法的doFrame

void doFrame(long frameTimeNanos, int frame, DisplayEventReceiver.VsyncEventData vsyncEventData) {
        final long startNanos;
        final long frameIntervalNanos = vsyncEventData.frameInterval;
        try {
            synchronized (mLock) {
                if (!mFrameScheduled) {
                    traceMessage("Frame not scheduled");
                    return; // no work to do
                }
                //预期时间,即Vsync信号发出的时间
                long intendedFrameTimeNanos = frameTimeNanos;
                //System.nanoTime()此方法提供纳秒级精度,常用于测量运行时间
                startNanos = System.nanoTime();
                //计算执行的时间差(虽然MessageQueue添加了同步屏障,但是当前有正在执行的同步任务,还是会导致doFrame延迟)
                final long jitterNanos = startNanos - frameTimeNanos;
                if (jitterNanos >= frameIntervalNanos) {
                    //计算掉帧数
                    final long skippedFrames = jitterNanos / frameIntervalNanos;
                    //掉帧数 >=30打印日志
                    if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
                        Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                                + "The application may be doing too much work on its main thread.");
                    }
                    //取模,得到一帧多出来的时间
                    final long lastFrameOffset = jitterNanos % frameIntervalNanos;
                    //进行绘制时间修正,保证每次绘制时间的间隔正确
                    frameTimeNanos = startNanos - lastFrameOffset;
                }
                 //如果底层传过来的时间(接收到Vsync信号时间)小于上一次绘制时间
                 if (frameTimeNanos < mLastFrameTimeNanos) {
                    if (DEBUG_JANK) {
                        Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                                + "previously skipped frame.  Waiting for next vsync.");
                    }
                    traceMessage("Frame time goes backward");
                    //跳过本次绘制,请求下一帧
                    scheduleVsyncLocked();
                    return;
                }
                //控制绘制频率
                if (mFPSDivisor > 1) {
                    long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
                    if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
                        traceMessage("Frame skipped due to FPSDivisor");
                        scheduleVsyncLocked();
                        return;
                    }
                }

                mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
                        vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval);
                //重置标志位,可以再次进入scheduleFrameLocked
                mFrameScheduled = false;
                //记录最后一帧的时间
                mLastFrameTimeNanos = frameTimeNanos;
                mLastFrameIntervalNanos = frameIntervalNanos;
                mLastVsyncEventData = vsyncEventData;
            }
            //下面就是按照类型顺序执行任务
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
             //1.INPUT类型任务
            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos);
            //2.ANIMATION类型任务
            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos);
             //3.INSETS_ANIMATION类型任务
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos,
                    frameIntervalNanos);
            //4.TRAVERSAL类型任务
            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos);
             //5.COMMIT类型任务
            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, frameIntervalNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

可以看到,Choreographer的引入,主要是配合Vsync,为上层App的渲染提供了稳定的时机,统一管理应用的输入、动画、绘制等任务的执行。

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

void doCallbacks(int callbackType, long frameTimeNanos, long frameIntervalNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
            // We use "now" to determine when callbacks become due because it's possible
            // for earlier processing phases in a frame to post callbacks that should run
            // in a following phase, such as an input event that causes an animation to start.
            final long now = System.nanoTime();
            /**
             *
             * 找到当前这一帧需要消费的callback回调,因为CallbackQueue是链表,返回头节点即可
             * - 根据callbackType类型从mCallbackQueues数组中获取对应的链表
             * - 传入当前时间作为参考点,从头遍历,找到第一个晚于当前时间的callback,将它设置为新的头结点,并断开这个新的头节点与前一个节点,返回之前的头结点
             *
             **/
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;

            // Update the frame time if necessary when committing the frame.
            // We only update the frame time if we are more than 2 frames late reaching
            // the commit phase.  This ensures that the frame time which is observed by the
            // callbacks will always increase from one frame to the next and never repeat.
            // We never want the next frame's starting frame time to end up being less than
            // or equal to the previous frame's commit frame time.  Keep in mind that the
            // next frame has most likely already been scheduled by now so we play it
            // safe by ensuring the commit time is always at least one frame behind.

            //CALLBACK_COMMIT是用来更新帧时间的,确保
            if (callbackType == Choreographer.CALLBACK_COMMIT) {
                final long jitterNanos = now - frameTimeNanos;
                Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
                //延迟到达2帧及以上时,才会更新帧时间,确保提交时间始终至少落后一帧来确保安全
                if (jitterNanos >= 2 * frameIntervalNanos) {
                    final long lastFrameOffset = jitterNanos % frameIntervalNanos
                            + frameIntervalNanos;
                    if (DEBUG_JANK) {
                        Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
                                + " ms which is more than twice the frame interval of "
                                + (frameIntervalNanos * 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方法
                c.run(frameTimeNanos);
            }
        } finally {
            synchronized (mLock) {
                mCallbacksRunning = false;
                do {
                    final CallbackRecord next = callbacks.next;
                    //回收callbacks
                    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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
        public void run(long frameTimeNanos) {
            if (token == FRAME_CALLBACK_TOKEN) {
                //Choreographer的postFrameCallback()中会传入token为FRAME_CALLBACK_TOKEN,会走到这里,通常用来计算丢帧情况
                ((FrameCallback)action).doFrame(frameTimeNanos);
            } else {
                 //取出Runnable执行run()
                ((Runnable)action).run();
            }
        }
    }

ViewRootImp中就使用了Choreographer:

	 ActivityThread#handleResumeActivity -> wm.addView
-> WindowManagerImpl#addView -> mGlobal.addView
-> WindowManagerGlobal#addView -> root.setView
-> ViewRootImpl#setView -> requestLayout() -> scheduleTraversals()

Activity启动走完onResume后,会进行Window的添加,Window添加过程中会调用ViewRootImpl的requestLayout()方法来请求绘制布局,requestLayout()方法内部又走到了scheduleTraversals()方法,先来看下这个方法

void scheduleTraversals() {
    if (!mTraversalScheduled) {
      	//标志位置为true,防止短时间内重复调用,View开始绘制渲染新页面之前恢复标志位。
        mTraversalScheduled = true;
       //添加同步屏障,保证Vsync到来立即执行刷新
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
       //调用Choreographer.postCallback,发送一个会在下一帧执行的回调,执行TraversalRunnable任务
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        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");
            }
	        //开始三大绘制流程
            performTraversals();

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

ViewRootImp中使用了Choreographer.postCallback()发送任务到队列中,监听Vsync信号,当有Vsync信号到来时,使用Handler发送异步消息,执行Runable任务。

小结:Choreographer源码分析流程图

Choreographer源码分析.jpg