Choreographer 编舞者的艺术

2023-08-06  本文已影响0人  Tsm_2020

想要了解Choreographer ,就需要知道他是干什么的,并且需要知道知道他内部主要成员有哪些,他们都分别负责什么.下面我先来简单介绍一下Choreographer以及他主要的成员变量

Choreographer

Choreographer 主要负责接受vsycs 垂直同步信号,并且在垂直同步信号来临时,将各个需要响应的绘制事件依照先后顺序一次执行,

成员变量 FrameHandler 用来响应垂直同步信号的事件

  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(System.nanoTime(), 0);
                  break;
              case MSG_DO_SCHEDULE_VSYNC:///
                  doScheduleVsync();
                  break;
              case MSG_DO_SCHEDULE_CALLBACK:
                  doScheduleCallback(msg.arg1);
                  break;
          }
      }
  }

FrameHandler 就是一个通过looper 来创建的Handler ,响应各个事件

成员变量 FrameDisplayEventReceiver接收到垂直同步信号后,使用 FrameHandler 来响应垂直同步事件


  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);
      }

      // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for
      // the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
      // for the internal display implicitly.
      @Override
      public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
          // Post the vsync event to the Handler.
          // The idea is to prevent incoming vsync events from completely starving
          // the message queue.  If there are no messages in the queue with timestamps
          // earlier than the frame time, then the vsync event will be processed immediately.
          // Otherwise, messages that predate the vsync event will be handled first.
          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;
          // 将自身发送出去
          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);
      }
  }

FrameDisplayEventReceiver 继承了 DisplayEventReceiver 来获得接收垂直同步信号的能力,同时他也实现了Runnable 接口,可以让他在接收到垂直同步信号的消息时使用FrameHandler 来发送一个自身的callback,也就是 run 中的方法开始刷新工作

成员变量 CallbackQueue[] 是一个数组,数组中的每一条数据时一个链表,记录着链表头,当有新的callback 加入到链表中时,根据时间来决定链表的插入顺序

Choreographer 为 CallbackQueue 定义了一个优先级,他们分别是

  /**
   * Callback type: Input callback.  Runs first.
   * @hide
   */
  public static final int CALLBACK_INPUT = 0;

  /**
   * Callback type: Animation callback.  Runs before {@link #CALLBACK_INSETS_ANIMATION}.
   * @hide
   */
  @TestApi
  public static final int CALLBACK_ANIMATION = 1;

  /**
   * Callback type: Animation callback to handle inset updates. This is separate from
   * {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via
   * {@link WindowInsetsAnimationController#changeInsets} for multiple ongoing animations but then
   * update the whole view system with a single callback to {@link View#dispatchWindowInsetsAnimationProgress}
   * that contains all the combined updated insets.
   * <p>
   * Both input and animation may change insets, so we need to run this after these callbacks, but
   * before traversals.
   * <p>
   * Runs before traversals.
   * @hide
   */
  public static final int CALLBACK_INSETS_ANIMATION = 2;

  /**
   * Callback type: Traversal callback.  Handles layout and draw.  Runs
   * after all other asynchronous messages have been handled.
   * @hide
   */
  public static final int CALLBACK_TRAVERSAL = 3;

那么在 CallbackQueue[] 这个数组中的顺序应该就是
CallbackQueue[0] input 队列
CallbackQueue[1] 动画队列
CallbackQueue[2] 添加动画队列
CallbackQueue[3] TRAVERSAL ,也就是在ViewRootImpl 中发送完消息屏障后,发送的callback ,


image.png

为什么说 CallbackQueue 是一个链表看下面的代码

image.png

一个很经典的链表插入操作,与MessageQueue类似,

下面我们再来看看 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();
  }
  
  // Thread local storage for the SF choreographer.
  private static final ThreadLocal<Choreographer> sSfThreadInstance =
          new ThreadLocal<Choreographer>() {
              @Override
              protected Choreographer initialValue() {
                  Looper looper = Looper.myLooper();
                  if (looper == null) {
                      throw new IllegalStateException("The current thread must have a looper!");
                  }
                  return new Choreographer(looper, VSYNC_SOURCE_SURFACE_FLINGER);
              }
          };


  private Choreographer(Looper looper, int vsyncSource) {
      mLooper = looper;
      mHandler = new FrameHandler(looper);
      mDisplayEventReceiver = USE_VSYNC
              ? new FrameDisplayEventReceiver(looper, vsyncSource)
              : null;
      mLastFrameTimeNanos = Long.MIN_VALUE;

      mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

      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 的创建和Looper类似,都是一个线程中只能有一个, 都保存在ThreadLocal中,
他的初始化过程中创建了 FrameHandler 和 FrameDisplayEventReceiver ,并且根据 getRefreshRate 帧率计算了刷新间隔,创建了一个 CallbackQueue 队列数组,

那么整个链路我们串起来看就应该是这样的,

1.ViewRootImpl 发送了一个消息屏障,并且在Choreographer中插入了一条 优先级为 CALLBACK_TRAVERSAL 的callback,用来执行测量 布局 绘制等工作

2. Choreographer 中的 FrameDisplayEventReceiver 接收到垂直同步信号的时候,先进行时间修正,将自身作为callback,使用 FrameHandler 发送一个异步消息,来执行自身的 run 方法,也就是 doFream() 方法

3. doFream 方法

  @UnsupportedAppUsage
  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) {
              // 计算丢了几帧
              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 {
        
          //按照实现约定好的顺序执行 CallbackQueue 中的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.");
      }
  }

这个方法先获取了一下系统时间,并且与这一帧的执行时间做比较,如果他们的差值大于每一帧的时间,那么证明丢帧了同时会在控制台打印 Skipped Frames log,
修正一下时间,如果执行时间比上一次已经执行的时间小,证明当前这一帧的是一个无用帧了,返回 ,
将这一帧的执行时间作为最后一帧的执行时间,
依次执行 CallbackQueue 链表数据中的callback,
这一帧结束

上一篇下一篇

猜你喜欢

热点阅读