腾讯性能监控框架Matrix源码分析(五)TracePlugin 之 Choreographer

535 阅读5分钟

在分析源码之前我们需要简单了解下Choreographer

Choreographer 顾名思义编舞者。用来接收硬件发送的 VSync(垂直同步) 信号,一般情况下,硬件每 16ms 发送一次。Choreographer,是一个Java类,包路径android.view.Choreographer。类注释是“协调动画、输入和绘图的计时”。 通常 应用层不会直接使用Choreographer,而是使用更高级的API,例如动画和View绘制相关的ValueAnimator.start()、View.invalidate()等。业界一般通过Choreographer来监控应用的帧率。

Choreographer初始化构造

private Choreographer(Looper looper, int vsyncSource) {
    //使用当前线程looper创建 mHandler
    mLooper = looper;
    mHandler = new FrameHandler(looper);
    //USE_VSYNC 4.1以上默认是true,表示 具备接受VSync的能力,这个接受能力就是FrameDisplayEventReceiver
    mDisplayEventReceiver = USE_VSYNC
            ? new FrameDisplayEventReceiver(looper, vsyncSource)
            : null;
    mLastFrameTimeNanos = Long.MIN_VALUE;
    //计算一帧的时间,Android手机屏幕是60Hz的刷新频率,就是16ms
    mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
    //也就是数组中有五个链表,每个链表存相同类型的任务:输入、动画、遍历绘制等任务(CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL)
    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 内部维护了一个 mCallbackQueues 数组,从名字上看它是一个队列。 其实它跟 HashMap 的结构有点像,每一个下标下都是一个链表。它的最大长度是 5,代表了五种不同类型的回调

  • CALLBACK_INPUT:输入回调;
  • CALLBACK_ANIMATION:动画回调;
  • CALLBACK_INSETS_ANIMATION:插入动画回调;
  • CALLBACK_TRAVERSAL:View 绘制遍历回调;
  • CALLBACK_COMMIT:提交回调。

外部可以通过 postCallback() 添加回调,可以自定义类型放置到合适的下标链表处。 虽然硬件每 16ms 发送一次 VSync 信号,但是想要接收到信号需要主动调用 nativeScheduleVsync() 方法。另外这个方法是一次性的,调用一次只会接收到一次信号。 所以在 postCallback() 添加回调后调用一次,等到接收到信号处理完逻辑后再次添加回调、请求信号,这样循环下去。

postCallback 最终调用 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;
        //获取对应类型任务
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime <= now) {
            //立即执行
            scheduleFrameLocked(now);
        } else {
            //延迟执行,最终也会走到scheduleFrameLocked
            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)把消息设置成异步,这是因为前面设置了同步屏障,只有异步消息才会执行。我们看下mHandler的对这个消息的处理

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

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

直接使用doScheduleCallback方法,看看:

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) {
           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()) {
               //申请 VSYNC 信号
               scheduleVsyncLocked();
           } else {
               // 若不在,就用mHandler发送消息到原线程,最后还是调用scheduleVsyncLocked方法
               Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
               msg.setAsynchronous(true);
               mHandler.sendMessageAtFrontOfQueue(msg);
           }
       } else {
           // 如果未开启VSYNC则直接doFrame方法(4.1后默认开启)
           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);
       }
   }
}
  • 如果系统未开启 VSYNC 机制,此时直接发送 MSG_DO_FRAME 消息到 FrameHandler。注意查看上面贴出的 FrameHandler 代码,此时直接执行 doFrame 方法。

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

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

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

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

很简单,调用mDisplayEventReceiver的scheduleVsync()方法,mDisplayEventReceiver是Choreographer构造方法中创建,是FrameDisplayEventReceiver 的实例。 FrameDisplayEventReceiver是 DisplayEventReceiver 的子类,DisplayEventReceiver 是一个 abstract class:

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

    mMessageQueue = looper.getQueue();  
   // 注册VSYNC信号监听者
    mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
            vsyncSource);

    mCloseGuard.open("dispose");
}

在 DisplayEventReceiver 的构造方法会通过 JNI 创建一个 IDisplayEventConnection 的 VSYNC 的监听者。

FrameDisplayEventReceiver的scheduleVsync()就是在 DisplayEventReceiver中:

@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 {
      nativeScheduleVsync(mReceiverPtr);
  }
}

scheduleVsync()就是使用native方法nativeScheduleVsync()去申请VSYNC信号。

那么信号是如何接收的呢,答案是在 DisplayEventReceiver 类:

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

    mMessageQueue = looper.getQueue();
    mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
            vsyncSource);

    mCloseGuard.open("dispose");
}

看上面的注释,这个方法是由 native 调用的,其实就是由这个方法接收发送来的 VSync 信号。timestampNanos 是指接收到信号的时间(纳秒)。

该类是抽象类,所以 Choreographer 有一个它的子类 FrameDisplayEventReceiver 负责处理接收到信号之后的逻辑:

 private final class FrameDisplayEventReceiver extends DisplayEventReceiver
          implements Runnable {
      @Override
      public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
...
          // 收到信号之后记录收到信号的时间
          mTimestampNanos = timestampNanos;
          mFrame = frame;
          Message msg = Message.obtain(mHandler, this);
          msg.setAsynchronous(true);
          // 再通过 Handler 定时执行当前线程
          mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
      }

      @Override
      public void run() {
          mHavePendingVsync = false;
          // 每一帧回调
          doFrame(mTimestampNanos, mFrame);
      }
  }

doFrame() 方法负责计算丢帧以及处理回调,调用者收到回调之后再次添加并请求接收信号。

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

      if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
          mDebugPrintNextFrameTimeDelta = false;
          Log.d(TAG, "Frame time delta: "
                  + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
      }

      long intendedFrameTimeNanos = frameTimeNanos;
      startNanos = System.nanoTime();
      //计算两帧之间的时间差
      final long jitterNanos = startNanos - frameTimeNanos;
      if (jitterNanos >= mFrameIntervalNanos) {
          //如果两帧大于认为的间隔 这里是16.6
          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.");
          }
          final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
          if (DEBUG_JANK) {
              Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
                      + "which is more than the frame interval of "
                      + (mFrameIntervalNanos * 0.000001f) + " ms!  "
                      + "Skipping " + skippedFrames + " frames and setting frame "
                      + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
          }
          frameTimeNanos = startNanos - lastFrameOffset;
      }

      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.");
          }
          scheduleVsyncLocked();
          return;
      }

      if (mFPSDivisor > 1) {
          long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
          if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
              scheduleVsyncLocked();
              return;
          }
      }

      mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
      mFrameScheduled = false;
      mLastFrameTimeNanos = frameTimeNanos;
  }

  try {
      //下面就是不同类型的CallBack回调
      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);
  }

  if (DEBUG_FRAMES) {
      final long endNanos = System.nanoTime();
      Log.d(TAG, "Frame " + frame + ": Finished, took "
              + (endNanos - startNanos) * 0.000001f + " ms, latency "
              + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
  }
}

接着看任务的具体执行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();
        }
    }
}

最后我们观察一下CallbackQueue链表中的Callback对象

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) {
        //如果是FRAME_CALLBACK_TOKEN之间执行doFrame
        if (token == FRAME_CALLBACK_TOKEN) {
            ((FrameCallback)action).doFrame(frameTimeNanos);
        } else {
            ((Runnable)action).run();
        }
    }
}

那么 啥时候 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);
}

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

Choreographer的postFrameCallback()通常用来计算丢帧情况,使用方式如下:

Choreographer.getInstance().postFrameCallback(new FPSFrameCallback(System.nanoTime()));
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);
    }
}

ViewRootImp 和 Choreographer

Choreographer 使用比较频繁的可能就是 ViewRootImp 了,我们知道 ViewRootImp 主要绘制方法 performTraversals() 就是封装在一个线程里的,而 Choreographer 的回调也可以是线程。

这样一想整个绘制流程也就比较清晰了:

20201216104059248.png

值得一提的是,ViewRootImp 绘制添加的回调类型是 CALLBACK_TRAVERSAL 表示执行绘制。此外 ViewRootImp 也封装了 CALLBACK_INPUT 线程处理输入事件、CALLBACK_ANIMATION 线程处理动画事件。

另外 Choreographer 回调事件的顺序是 CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL。这么做的原因也好理解,输入事件和动画会改变 View 视图,所以绘制放到最后一次性把 View 展示出来。

使用Choreographer的postCallback()、postFrameCallback() 作用理解:发送任务 存队列中,监听VSync信号,当前VSync到来时 会使用mHandler发送异步message,这个message的Runnable就是队列中的所有任务

20200821112357259.webp