美文网首页
Choreographer 编舞者的艺术

Choreographer 编舞者的艺术

作者: Tsm_2020 | 来源:发表于2023-08-06 12:04 被阅读0次

    想要了解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,
    这一帧结束

    相关文章

      网友评论

          本文标题:Choreographer 编舞者的艺术

          本文链接:https://www.haomeiwen.com/subject/lkaipdtx.html