Andorid-Choreographer 工作原理

569 阅读17分钟

Android显示系统 

显示系统

CPU : 负责计算帧数据,把计算好的数据交给 GPU

GPU : 对图形数据进行渲染,渲染好后放到 buffer (图像缓冲区)中存起来

Display : 屏幕或者显示器,负责把 buffer 中的数据呈现到屏幕上

Choreographer

编舞者,用来控制当收到 VSync 信号后才开始绘制任务,保证绘制拥有完整的 16.6 ms.

入口: scheduleTraversals

在调用 startActivity 后, 经过 AMS 的一些处理, 后面又通过 Binder 调用目标进程的 ActivityThread.handleResumeActivity 方法,在这个方法里会回调目标 Activity 的 onResume 和 makeVisible 方法, 在 makeVisible 方法里完成 WindowManager.addView 的过程,这个过程调用了 ViewRootImpl.setView 方法, 内部又调用其 scheduleTraversals 方法,最后会走到 perfromTraversals 方法,接着就到了 measure, layout, draw 三大流程了。

//  frameworks/base/core/java/android/view/ViewRootImpl.java

2351      @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2352      void scheduleTraversals() {
2353          if (!mTraversalScheduled) {
                  // 保证同时间多次更改只会刷新一次
2354              mTraversalScheduled = true;
2355              // nubia modify for app launch traversals accelerate
2356              //mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
2357              if (!ActivityThread.ZTE_TRAVERSAL_ACCELERATE_ENABLED || !mHasStartTraversalsAccelerate) {
                      // 添加同步屏障,保证Vsync 到来立即执行绘制
2358                  mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
2359              } else {
2360                  Log.d(mTag, "scheduleTraversals ,mHasStartTraversalsAccelerate: " + mHasStartTraversalsAccelerate);
                      
2361                  mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrierAtFrontByZTE();
2362              }
2363              // nubia modify end
2364              //none-ui thread postSyncBarrie may case block,check it here
2365              if (mThread != Thread.currentThread()) {
2366                  Slog.w(mTag, "postSyncBarrier() from None-UIThread!,vripl=" + this
2367                          + ",barrier=" + mTraversalBarrier
2368                          + ",t=" + mThread + ",curT=" + Thread.currentThread());
2369              }
2370              mChoreographer.postCallback(
2371                      Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
2372              notifyRendererOfFramePending();
2373              pokeDrawLockIfNeeded();
2374          }
2375      }
...
9059      final class TraversalRunnable implements Runnable {
9060          @Override
9061          public void run() {
9062              doTraversal();
9063          }
9064      }
9065      final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
...
...
2400      void doTraversal() {
2401          if (mTraversalScheduled) {
2402              mTraversalScheduled = false;
2403              // nubia modify for app launch traversals accelerate
2404              //mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
2405              if (ActivityThread.ZTE_TRAVERSAL_ACCELERATE_ENABLED) {
2406                  if (!mHasStartTraversalsAccelerate) {
2407                      if (mHandler.getLooper().getQueue().checkHasSyncBarrier(mTraversalBarrier)) {
                              // 移除同步屏障 
2408                          mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
2409                      }
2410                  }
2411              } else {
2412                  mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
2413              }
2414              // nubia modify end
2415              if (mProfile) {
2416                  Debug.startMethodTracing("ViewAncestor");
2417              }
2418              // 真正执行 View  measure , layout, draw 流程
2419              performTraversals();
2420  
2421              if (mProfile) {
2422                  Debug.stopMethodTracing();
2423                  mProfile = false;
2424              }
2425          }
2426      }

首先使用 mTraversalScheduled 字段保证同时间多次更改只会刷新一次。

然后为当前线程的 MessageQueue 添加同步屏障,来屏蔽同步消息,保证 VSync 到来后立即执行绘制,而不是等前面的同步消息。

调用 mChoreographer.postCallback() 方法发送了一个会在下一帧执行的回调,即在下一个 VSync 到来时会执行 TraversalRunnable->doTrvaersal()->performTraversals()->绘制流程。

Choreographer 实例化

// frameworks/base/core/java/android/view/Choreographer.java95  public final class Choreographer {
96      private static final String TAG = "Choreographer";
97  
98      // Prints debug messages about jank which was detected (low volume).
99      private static final boolean DEBUG_JANK = false;
100      private static final boolean OPTS_INPUT = true;
101  
102      // Prints debug messages about every frame and callback registered (high volume).
103      private static final boolean DEBUG_FRAMES = false;
104  
105      // The default amount of time in ms between animation frames.
106      // When vsync is not enabled, we want to have some idea of how long we should
107      // wait before posting the next animation message.  It is important that the
108      // default value be less than the true inter-frame delay on all devices to avoid
109      // situations where we might skip frames by waiting too long (we must compensate
110      // for jitter and hardware variations).  Regardless of this value, the animation
111      // and display loop is ultimately rate-limited by how fast new graphics buffers can
112      // be dequeued.
113      private static final long DEFAULT_FRAME_DELAY = 10;
114      private static final int TOP_APP_ADJ = 0;
115      private static final int PERCEPTIBLE_APP_ADJ = 200;
116  
117      // The number of milliseconds between animation frames.
118      private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY;
119  
120      // Thread local storage for the choreographer.
121      private static final ThreadLocal<Choreographer> sThreadInstance =
122              new ThreadLocal<Choreographer>() {
123          @Override
124          protected Choreographer initialValue() {
125              Looper looper = Looper.myLooper();
126              if (looper == null) {
127                  throw new IllegalStateException("The current thread must have a looper!");
128              }
129              Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
130              if (looper == Looper.getMainLooper()) {
131                  mMainInstance = choreographer;
132              }
133              return choreographer;
134          }
135      };
136  
137      private static volatile Choreographer mMainInstance;
138  
139      // Thread local storage for the SF choreographer.
140      private static final ThreadLocal<Choreographer> sSfThreadInstance =
141              new ThreadLocal<Choreographer>() {
142                  @Override
143                  protected Choreographer initialValue() {
144                      Looper looper = Looper.myLooper();
145                      if (looper == null) {
146                          throw new IllegalStateException("The current thread must have a looper!");
147                      }
148                      return new Choreographer(looper, VSYNC_SOURCE_SURFACE_FLINGER);
149                  }
150              };
    
    
    ...
        
 300      private Choreographer(Looper looper, int vsyncSource) {
301          mLooper = looper;
             // 使用当前线程 looper 创建 Handler
302          mHandler = new FrameHandler(looper);
303          mDisplayEventReceiver = USE_VSYNC
304                  ? new FrameDisplayEventReceiver(looper, vsyncSource)
305                  : null;
306          mLastFrameTimeNanos = Long.MIN_VALUE;
307          // 计算一帧的时间,即16ms
308          mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
309          ScrollOptimizer.setFrameInterval(mFrameIntervalNanos);
310  
311          mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
312          for (int i = 0; i <= CALLBACK_LAST; i++) {
313              mCallbackQueues[i] = new CallbackQueue();
314          }
315          // b/68769804: For low FPS experiments.
316          setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
317      }

Choreographer 中共有四种 callbackType:

// 
    /**
243       * Must be kept in sync with CALLBACK_* ints below, used to index into this array.
244       * @hide
245       */
246      private static final String[] CALLBACK_TRACE_TITLES = {
247              "input", "animation", "insets_animation", "traversal", "commit"
248      };
249  
250      /**
251       * Callback type: Input callback.  Runs first.
252       * @hide
253       */
         // 输入事件
254      public static final int CALLBACK_INPUT = 0;
255  
256      /**
257       * Callback type: Animation callback.  Runs before  {@link #CALLBACK_INSETS_ANIMATION}.
258       * @hide
259       */
260      @TestApi // 动画
261      public static final int CALLBACK_ANIMATION = 1;
262  
263      /**
264       * Callback type: Animation callback to handle inset updates. This is separate from
265       * {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via
266       * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} for multiple
267       * ongoing animations but then update the whole view system with a single callback to
268       * {@link View#dispatchWindowInsetsAnimationProgress} that contains all the combined updated
269       * insets.
270       * <p>
271       * Both input and animation may change insets, so we need to run this after these callbacks, but
272       * before traversals.
273       * <p>
274       * Runs before traversals.
275       * @hide
276       */
        // 窗口刷新, 执行 measure / layout / draw 操作
277      public static final int CALLBACK_INSETS_ANIMATION = 2;
278  
279      /**
280       * Callback type: Traversal callback.  Handles layout and draw.  Runs
281       * after all other asynchronous messages have been handled.
282       * @hide
283       */
284      public static final int CALLBACK_TRAVERSAL = 3;
285  
286      /**
287       * Callback type: Commit callback.  Handles post-draw operations for the frame.
288       * Runs after traversal completes.  The {@link #getFrameTime() frame time} reported
289       * during this callback may be updated to reflect delays that occurred while
290       * traversals were in progress in case heavy layout operations caused some frames
291       * to be skipped.  The frame time reported during this callback provides a better
292       * estimate of the start time of the frame in which animations (and other updates
293       * to the view hierarchy state) actually took effect.
294       * @hide
295       */
296      public static final int CALLBACK_COMMIT = 4;
297  
298      private static final int CALLBACK_LAST = CALLBACK_COMMIT;
299  

这四种类型的任务存入对应类型的 CallbackQueue 中, 每当收到 VSYNC 信号时,Choreographer 将按顺序处理这些类型的任务。

// FrameHandler ,它用来处理异步消息
1113      private final class FrameHandler extends Handler {
1114          public FrameHandler(Looper looper) {
1115              super(looper);
1116          }
1117  
1118          @Override
1119          public void handleMessage(Message msg) {
1120              switch (msg.what) {
1121                  case MSG_DO_FRAME:
1122                      doFrame(System.nanoTime(), 0, new DisplayEventReceiver.VsyncEventData());
1123                      break;
1124                  case MSG_DO_SCHEDULE_VSYNC:
1125                      doScheduleVsync();
1126                      break;
1127                  case MSG_DO_SCHEDULE_CALLBACK:
1128                      doScheduleCallback(msg.arg1);
1129                      break;
1130                  case MSG_DO_CHECK_BACKGROUND:
1131                      doCheckBackgroundDrawing();
1132                      break;
1133              }
1134          }
1135      }
1136  

Vsync 信号注册

DisplayEventReceiver

mDisplayEventReceiver 是 FrameDisplayEventReceiver 类型的实例。

在 Choreographer 构造方法中实例化,其父类为 DisplayEventReceiver。

//frameworks/base/core/java/android/view/DisplayEventReceiver.java 
​
​
82      private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver,
83              MessageQueue messageQueue, int vsyncSource, int eventRegistration);
        ...
106      public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration) {
107          if (looper == null) {
108              throw new IllegalArgumentException("looper must not be null");
109          }
110  
111          mMessageQueue = looper.getQueue();
             // 注册  VSYNC 信号监听者
112          mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
113                  vsyncSource, eventRegistration);
114      }

nativeInit

nativeInit 是一个 native 方法,其实现在 android_view_DisplayEventReceiver.cpp

// frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp178  static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj,
179                          jint vsyncSource, jint eventRegistration) {
180      sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
181      if (messageQueue == NULL) {
182          jniThrowRuntimeException(env, "MessageQueue is not initialized.");
183          return 0;
184      }
185  
186      sp<NativeDisplayEventReceiver> receiver =
187              new NativeDisplayEventReceiver(env, receiverWeak, messageQueue, vsyncSource,
188                                             eventRegistration);
189      status_t status = receiver->initialize();
190      if (status) {
191          String8 message;
192          message.appendFormat("Failed to initialize display event receiver.  status=%d", status);
193          jniThrowRuntimeException(env, message.string());
194          return 0;
195      }
196  
197      receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
198      return reinterpret_cast<jlong>(receiver.get());
199  }

NativeDisplayEventReceiver 继承于 DisplayEventDispatcher

// frameworks/native/libs/gui/DisplayEventReceiver.cpp 35  DisplayEventReceiver::DisplayEventReceiver(
36          ISurfaceComposer::VsyncSource vsyncSource,
37          ISurfaceComposer::EventRegistrationFlags eventRegistration) {
38      sp<ISurfaceComposer> sf(ComposerService::getComposerService());
39      if (sf != nullptr) {
40          mEventConnection = sf->createDisplayEventConnection(vsyncSource, eventRegistration);
41          if (mEventConnection != nullptr) {
42              mDataChannel = std::make_unique<gui::BitTube>();
43              mEventConnection->stealReceiveChannel(mDataChannel.get());
44          }
45      }
46  }
​
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
        ISurfaceComposer::VsyncSource vsyncSource) {
    if (vsyncSource == eVsyncSourceSurfaceFlinger) {
        return mSFEventThread->createEventConnection();
    } else {
        // vsyncSource 是 APP
        return mEventThread->createEventConnection();
    }
}

EventThread.createEventConnection 创建了一个对 Vsync 信号感兴趣的连接。

//  frameworks/native/libs/gui/DisplayEventDispatcher.cpp43  status_t DisplayEventDispatcher::initialize() {
44      status_t result = mReceiver.initCheck();
45      if (result) {
46          ALOGW("Failed to initialize display event receiver, status=%d", result);
47          return result;
48      }
49  
50      if (mLooper != nullptr) {
             // 用来监听 mReceiver 所获取的文件句柄,当有存在对 Vsync 信号感兴趣的连接且接收到了 Vsync 信号时
             //  会发送数据到 mReceiver,然后回调 DisplayEventDispatcher 中的 handleEvent 
51          int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL);
52          if (rc < 0) {
53              return UNKNOWN_ERROR;
54          }
55      }
56  
57      return OK;
58  }
59 
    
97  int DisplayEventDispatcher::getFd() const {
98      return mReceiver.getFd();
99  }
​
​
// frameworks/native/libs/gui/DisplayEventReceiver.cpp 
57  int DisplayEventReceiver::getFd() const {
58      if (mDataChannel == nullptr)
59          return NO_INIT;
60  
61      return mDataChannel->getFd();
62  }

mReceiver 是 DisplayEventReceiver 类型实例,位于 frameworks/native/libs/gui/DisplayEventReceiver.cpp

Looper.addFd

BitTube

是 Android 提供的一种进程通讯方式,它通过socketpair 来实现全双工的通讯。

在 BitTube 对象中有两个文件描述符: mReceivedFd 和 mSendFd, 以及 read 和 write 分别用来进行读写操作。

获取两个文件描述符的函数:

int BitTube::getFd() const {
    return mReceivedFd;
}
​
int BitTube::getSendFd() const {
    return mSendFd;
}

跨进程

上面 Choreographer 中的 mEventConnection 是 IDisplayEventConnection 类型,是一个 native 层的 Binder 代理对象,用来跟 SurfaceFlinger 跨进程通信,可以看到它通过 stealReceiveChannel 方法把 APP 进程端创建的 BitTube 指针传给了 SF 进程。

然后在 APP 进程又通过 Looper->addFd(mReceiver.getFd(), ...) 方法监听 mReceiveFd 描述符的数据,当 Vsync 信号到来后,SF 对 BitTube 做 send 操作,则 Looper 监听到对应 mReceiveFd 中的数据变化,于是触发 DisplayEventDispatcher.handleEvent 方法,该方法中会调用 mReceiver(DisplayEventReceiver).getEvents 方法,这个方法会调用 BitTube.recvObjects 拿到 SF 进程传来的数据(由于 Looper 触发了监听,此时 recvObjects 肯定能拿到数据,而不会阻塞),即表示 Vsync 信号来了。

请求 Vsync 信号

Choreographer.postCallback

mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)

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();
        final long dueTime = now + delayMillis;
        // 对应类型的CallbackQueue添加Callback
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
​
        if (dueTime <= now) {
            // 立即执行
            scheduleFrameLocked(now);
        } else {
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            msg.setAsynchronous(true);
            // handleMessage会调用doScheduleCallback(msg.arg1)方法
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}
​
void doScheduleCallback(int callbackType) {
    synchronized (mLock) {
        if (!mFrameScheduled) {
            final long now = SystemClock.uptimeMillis();
            if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
                scheduleFrameLocked(now);
            }
        }
    }
}

可以看到延迟与否都会调用 scheduleFrameLocked 方法。

Choreographer.scheduleFrameLocked

// frameworks/base/core/java/android/view/Choreographer.java
​
private void scheduleFrameLocked(long now) {
710          if (!mFrameScheduled) {
711              mFrameScheduled = true;
712              if (OPTS_INPUT) {
713                  if (!mIsVsyncScheduled) {
714                      long curr = System.nanoTime();
715                      boolean skipFlag = curr - mLastTouchOptTimeNanos < mFrameIntervalNanos;
716                      Trace.traceBegin(Trace.TRACE_TAG_VIEW, "scheduleFrameLocked-mMotionEventType:"
717                                       + mMotionEventType + " mTouchMoveNum:"+ mTouchMoveNum
718                                       + " mConsumedDown:" + mConsumedDown
719                                       + " mConsumedMove:" + mConsumedMove
720                                       + " mIsDoFrameProcessing:" + mIsDoFrameProcessing
721                                       + " skip:" + skipFlag
722                                       + " diff:" + (curr - mLastTouchOptTimeNanos));
723                      Trace.traceEnd(Trace.TRACE_TAG_VIEW);
724                      synchronized(this) {
725                          switch(mMotionEventType) {
726                              case MOTION_EVENT_ACTION_DOWN:
727                                  mConsumedMove = false;
728                                  if (!mConsumedDown && !skipFlag && !mIsDoFrameProcessing) {
729                                      Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
730                                      msg.setAsynchronous(true);
731                                      mHandler.sendMessageAtFrontOfQueue(msg);
732                                      mLastTouchOptTimeNanos = System.nanoTime();
733                                      mConsumedDown = true;
734                                      return;
735                                  }
736                                  break;
737                              case MOTION_EVENT_ACTION_MOVE:
738                                  mConsumedDown = false;
739                                  //if ((mTouchMoveNum == 1) && !mConsumedMove && !skipFlag) {
740                                  if (!mConsumedMove && !skipFlag && !mIsDoFrameProcessing) {
741                                      Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
742                                      msg.setAsynchronous(true);
743                                      mHandler.sendMessageAtFrontOfQueue(msg);
744                                      mLastTouchOptTimeNanos = System.nanoTime();
745                                      mConsumedMove = true;
746                                      return;
747                                  }
748                                  break;
749                              case MOTION_EVENT_ACTION_UP:
750                              case MOTION_EVENT_ACTION_CANCEL:
751                                  mConsumedMove = false;
752                                  mConsumedDown = false;
753                                  break;
754                              default:
755                                  break;
756                          }
757                      }
758                  }
759              }
760              if (ZFrameMetricsListener.ZTE_FEATURE_ZFRAMEMETRICS && ZFrameMetricsListener.getInstance() != null) {
761                  Trace.traceBegin(Trace.TRACE_TAG_VIEW, "scheduleFrameLocked reportDrawEvent");
762                  ZFrameMetricsListener.getInstance().reportDrawEvent();
763                  Trace.traceEnd(Trace.TRACE_TAG_VIEW);
764              }
765              if (ScrollOptimizer.shouldUseVsync(USE_VSYNC)) {
766                  if (DEBUG_FRAMES) {
767                      Log.d(TAG, "Scheduling next frame on vsync.");
768                  }
769  
770                  // If running on the Looper thread, then schedule the vsync immediately,
771                  // otherwise post a message to schedule the vsync from the UI thread
772                  // as soon as possible.
773                  if (!mVisible && !mBackgroundDrawing)
774                      mBackgroundFrameCount++;
775  
776                  if (!mBackgroundDrawing) {
777                      if (isRunningOnLooperThreadLocked()) {
778                          scheduleVsyncLocked();
779                      } else {
780                          Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
781                          msg.setAsynchronous(true);
782                          mHandler.sendMessageAtFrontOfQueue(msg);
783                      }
784                  } else {
785                      Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
786                      msg.setAsynchronous(true);
787                      mHandler.sendMessageDelayed(msg, 100);
788                  }
789              } else {
790                  sFrameDelay = ScrollOptimizer.getFrameDelay(sFrameDelay,
791                          mLastFrameTimeNanos);
792                  final long nextFrameTime = Math.max(
793                          mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
794                  if (DEBUG_FRAMES) {
795                      Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
796                  }
797                  Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
798                  msg.setAsynchronous(true);
799                  mHandler.sendMessageAtTime(msg, nextFrameTime);
800              }
801          }
802      }
803 
    
    ...
1015      void doScheduleVsync() {
1016          synchronized (mLock) {
1017              if (mFrameScheduled) {
1018                  scheduleVsyncLocked();
1019              }
1020          }
1021      }
​
    ...
1047      private void scheduleVsyncLocked() {
1048          try {
1049              Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#scheduleVsyncLocked");
1050              mDisplayEventReceiver.scheduleVsync();
1051              mIsVsyncScheduled = true;
1052          } finally {
1053              Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1054          }
1055      }
1056          

DisplayEventReceiver.scheduleVsync

​
​
public void scheduleVsync() {
    if (mReceiverPtr == 0) {
        // ...
    } else {
        nativeScheduleVsync(mReceiverPtr);
    }
}
​
// frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
    sp<NativeDisplayEventReceiver> receiver = reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
    status_t status = receiver->scheduleVsync();
    // ...
}
​
// frameworks/base/libs/androidfw/DisplayEventDispatcher.cpp
status_t DisplayEventDispatcher::scheduleVsync() {
    if (!mWaitingForVsync) {
        // Drain all pending events.
        nsecs_t vsyncTimestamp;
        int32_t vsyncDisplayId;
        uint32_t vsyncCount;
        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
        }
​
        status_t status = mReceiver.requestNextVsync();
        mWaitingForVsync = true;
    }
    return OK;
}
​
// frameworks/native/libs/gui/DisplayEventReceiver.cpp
status_t DisplayEventReceiver::requestNextVsync() {
    if (mEventConnection != NULL) {
        // 请求接收下一次Vsync信号的回调
        // 可以唤醒 EventThread 线程,等到 Vsync 信号到来后回调给 APP
        mEventConnection->requestNextVsync();
        return NO_ERROR;
    }
    return NO_INIT;
}

Vsync 回调流程

DisplayEventDispatcher::handleEvent

//frameworks/native/libs/gui/DisplayEventDispatcher.cpp101  int DisplayEventDispatcher::handleEvent(int, int events, void*) {
102      if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
103          ALOGE("Display event receiver pipe was closed or an error occurred.  "
104                "events=0x%x",
105                events);
106          return 0; // remove the callback
107      }
108  
109      if (!(events & Looper::EVENT_INPUT)) {
110          ALOGW("Received spurious callback for unhandled poll event.  "
111                "events=0x%x",
112                events);
113          return 1; // keep the callback
114      }
115  
116      // Drain all pending events, keep the last vsync.
117      nsecs_t vsyncTimestamp;
118      PhysicalDisplayId vsyncDisplayId;
119      uint32_t vsyncCount;
120      VsyncEventData vsyncEventData;
         // 清除所有的 pending 事件,只保留最后一次 vsync 
121      if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) {
122          ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
123                ", displayId=%s, count=%d, vsyncId=%" PRId64,
124                this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount,
125                vsyncEventData.id);
126          mWaitingForVsync = false;
127          dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
128      }
129  
130      return 1; // keep the callback
131  }
132  
    
    
    
   // frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
    
101  void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
102                                                 uint32_t count, VsyncEventData vsyncEventData) {
103      JNIEnv* env = AndroidRuntime::getJNIEnv();
104  
105      ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
106      if (receiverObj.get()) {
107          ALOGV("receiver %p ~ Invoking vsync handler.", this);
108          env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
109                              timestamp, displayId.value, count, vsyncEventData.id,
110                              vsyncEventData.deadlineTimestamp, vsyncEventData.frameInterval);
111          ALOGV("receiver %p ~ Returned from vsync handler.", this);
112      }
113  
114      mMessageQueue->raiseAndClearException(env, "dispatchVsync");
115  }
116  
​
    ...
233  int register_android_view_DisplayEventReceiver(JNIEnv* env) {
234      int res = RegisterMethodsOrDie(env, "android/view/DisplayEventReceiver", gMethods,
235                                     NELEM(gMethods));
236  
237      jclass clazz = FindClassOrDie(env, "android/view/DisplayEventReceiver");
238      gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
239  
240      gDisplayEventReceiverClassInfo.dispatchVsync =
241              GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync",
242                               "(JJIJJJ)V");
243      gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
244              gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
245      gDisplayEventReceiverClassInfo.dispatchModeChanged =
246              GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchModeChanged",
247                               "(JJI)V");
248      gDisplayEventReceiverClassInfo.dispatchFrameRateOverrides =
249              GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz,
250                               "dispatchFrameRateOverrides",
251                               "(JJ[Landroid/view/DisplayEventReceiver$FrameRateOverride;)V");
252  
253      jclass frameRateOverrideClazz =
254              FindClassOrDie(env, "android/view/DisplayEventReceiver$FrameRateOverride");
255      gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.clazz =
256              MakeGlobalRefOrDie(env, frameRateOverrideClazz);
257      gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.init =
258              GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.clazz,
259                               "<init>", "(IF)V");
260  
261      return res;
262  }

由上可知,回调到 Java 层android/view/DisplayEventReceiver 的 dispatchVsync 方法:

256      // Called from native code.
257      @SuppressWarnings("unused")
258      private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
259              long frameTimelineVsyncId, long frameDeadline, long frameInterval) {
260          onVsync(timestampNanos, physicalDisplayId, frame,
261                  new VsyncEventData(frameTimelineVsyncId, frameDeadline, frameInterval));
262      }
263  
    private final class FrameDisplayEventReceiver extends DisplayEventReceiver
1138              implements Runnable {
1139          private boolean mHavePendingVsync;
1140          private long mTimestampNanos;
1141          private int mFrame;
1142          private VsyncEventData mLastVsyncEventData = new VsyncEventData();
1143  
1144          public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
1145              super(looper, vsyncSource, 0);
1146          }
1147  
1148          // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for
1149          // the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
1150          // for the internal display implicitly.
1151          @Override
1152          public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
1153                  VsyncEventData vsyncEventData) {
1154              try {
1155                  if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
1156                      Trace.traceBegin(Trace.TRACE_TAG_VIEW,
1157                              "Choreographer#onVsync " + vsyncEventData.id);
1158                  }
1159                  // Post the vsync event to the Handler.
1160                  // The idea is to prevent incoming vsync events from completely starving
1161                  // the message queue.  If there are no messages in the queue with timestamps
1162                  // earlier than the frame time, then the vsync event will be processed immediately.
1163                  // Otherwise, messages that predate the vsync event will be handled first.
1164                  long now = System.nanoTime();
1165                  if (timestampNanos > now) {
1166                      Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
1167                              + " ms in the future!  Check that graphics HAL is generating vsync "
1168                              + "timestamps using the correct timebase.");
1169                      timestampNanos = now;
1170                  }
1171  
1172                  if (mHavePendingVsync) {
1173                      Log.w(TAG, "Already have a pending vsync event.  There should only be "
1174                              + "one at a time.");
1175                  } else {
1176                      mHavePendingVsync = true;
1177                  }
1178  
1179                  mTimestampNanos = timestampNanos;
1180                  mFrame = frame;
1181                  mLastVsyncEventData = vsyncEventData;
1182                  ScrollOptimizer.setVsyncTime(mTimestampNanos);
                      // 会调用 run 方法
1183                  Message msg = Message.obtain(mHandler, this);
1184                  msg.setAsynchronous(true);
1185                  mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
1186              } finally {
1187                  Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1188              }
1189          }
1190  
1191          @Override
1192          public void run() {
1193              mHavePendingVsync = false;
1194              doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
1195          }
1196      }
1197  

Choreographer.doFrame

Choreographer.doFrame

​
  void doFrame(long frameTimeNanos, int frame,
839              DisplayEventReceiver.VsyncEventData vsyncEventData) {
840          final long startNanos;
841          final long frameIntervalNanos = vsyncEventData.frameInterval;
842          try {
843              mIsDoFrameProcessing = true;
844              if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
845                  Trace.traceBegin(Trace.TRACE_TAG_VIEW,
846                          "Choreographer#doFrame " + vsyncEventData.id);
847              }
848              synchronized (mLock) {
849                  mIsVsyncScheduled = false;
850                  if (!mFrameScheduled) {
851                      traceMessage("Frame not scheduled");
852                      return; // no work to do
853                  }
854  
855                  if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
856                      mDebugPrintNextFrameTimeDelta = false;
857                      Log.d(TAG, "Frame time delta: "
858                              + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
859                  }
860                  // 计划执行时间
861                  long intendedFrameTimeNanos = frameTimeNanos;
862                  startNanos = System.nanoTime();
863                  final long jitterNanos = startNanos - frameTimeNanos;
864                  if (jitterNanos >= frameIntervalNanos) {
                         // 是否超过一帧的时间,因为虽然添加了同步屏障,但是还是有正在执行的同步任务,导致doFrame 延迟执行了
                         // 计算掉帧数
865                      final long skippedFrames = jitterNanos / frameIntervalNanos;
866                      if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
                             // 默认掉帧超过30帧打印日志
867                          Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
868                                  + "The application may be doing too much work on its main thread.");
869                          try {
870                              if (getSceneDecisionManager() != null) {
871                                  getSceneDecisionManager().monitorFrameSkipped(skippedFrames, Process.myPid());
872                              }
873                          } catch (Exception e) {
874                              // do nothing
875                          }
876                      }
877                      final long lastFrameOffset = jitterNanos % frameIntervalNanos;
878                      if (DEBUG_JANK) {
879                          Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
880                                  + "which is more than the frame interval of "
881                                  + (frameIntervalNanos * 0.000001f) + " ms!  "
882                                  + "Skipping " + skippedFrames + " frames and setting frame "
883                                  + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
884                      }
885                      frameTimeNanos = startNanos - lastFrameOffset;
886                  }
887  
888                  if (frameTimeNanos < mLastFrameTimeNanos) {
889                      if (DEBUG_JANK) {
890                          Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
891                                  + "previously skipped frame.  Waiting for next vsync.");
892                      }
893                      traceMessage("Frame time goes backward");
894                      scheduleVsyncLocked();
895                      return;
896                  }
897  
898                  if (mFPSDivisor > 1) {
899                      long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
900                      if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
901                          traceMessage("Frame skipped due to FPSDivisor");
902                          scheduleVsyncLocked();
903                          return;
904                      }
905                  }
906  
907                  mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
908                          vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval);
909                  mFrameScheduled = false;
910                  mLastFrameTimeNanos = frameTimeNanos;
911                  if (false && ZFrameMetricsListener.ZTE_FEATURE_ZFRAMEMETRICS
912                          && ZFrameMetricsListener.getInstance() != null) {
913                      if (frameIntervalNanos > 0 && frameIntervalNanos != mLastFrameIntervalNanos) {
914                          ZFrameMetricsListener.getInstance().reportFrameRateChange((long) (frameIntervalNanos * 0.000001f));
915                      }
916                  }
917                  mLastFrameIntervalNanos = frameIntervalNanos;
918                  mLastVsyncEventData = vsyncEventData;
919              }
920  
921              ScrollOptimizer.setUITaskStatus(true);
922              AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
923  
924              mFrameInfo.markInputHandlingStart();
925              doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos);
926  
927              mFrameInfo.markAnimationsStart();
928              doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos);
929              doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos,
930                      frameIntervalNanos);
931  
932              mFrameInfo.markPerformTraversalsStart();
933              doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos);
934  
935              doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, frameIntervalNanos);
936              ScrollOptimizer.setUITaskStatus(false);
937          } finally {
938              AnimationUtils.unlockAnimationClock();
939              Trace.traceEnd(Trace.TRACE_TAG_VIEW);
940          }
941  
942          if (DEBUG_FRAMES) {
943              final long endNanos = System.nanoTime();
944              Log.d(TAG, "Frame " + frame + ": Finished, took "
945                      + (endNanos - startNanos) * 0.000001f + " ms, latency "
946                      + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
947          }
948          mIsDoFrameProcessing = false;
949      }

Choreographer.doCallbacks

Choreographer.doCallbacks

 void doCallbacks(int callbackType, long frameTimeNanos, long frameIntervalNanos) {
952          CallbackRecord callbacks;
953          synchronized (mLock) {
954              // We use "now" to determine when callbacks become due because it's possible
955              // for earlier processing phases in a frame to post callbacks that should run
956              // in a following phase, such as an input event that causes an animation to start.
957              final long now = System.nanoTime();
                 // 根据指定的类型callbackQueue 中查找到达执行时间的 CallbackRecord
958              callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
959                      now / TimeUtils.NANOS_PER_MS);
960              if (callbacks == null) {
961                  return;
962              }
963              mCallbacksRunning = true;
964  
965              // Update the frame time if necessary when committing the frame.
966              // We only update the frame time if we are more than 2 frames late reaching
967              // the commit phase.  This ensures that the frame time which is observed by the
968              // callbacks will always increase from one frame to the next and never repeat.
969              // We never want the next frame's starting frame time to end up being less than
970              // or equal to the previous frame's commit frame time.  Keep in mind that the
971              // next frame has most likely already been scheduled by now so we play it
972              // safe by ensuring the commit time is always at least one frame behind.
973              if (callbackType == Choreographer.CALLBACK_COMMIT) {
974                  final long jitterNanos = now - frameTimeNanos;
975                  Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
976                  if (jitterNanos >= 2 * frameIntervalNanos) {
                         // 当commit类型回调执行的时间点超过2帧,则更新mLastFrameTimeNanos
977                      final long lastFrameOffset = jitterNanos % frameIntervalNanos
978                              + frameIntervalNanos;
979                      if (DEBUG_JANK) {
980                          Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
981                                  + " ms which is more than twice the frame interval of "
982                                  + (frameIntervalNanos * 0.000001f) + " ms!  "
983                                  + "Setting frame time to " + (lastFrameOffset * 0.000001f)
984                                  + " ms in the past.");
985                          mDebugPrintNextFrameTimeDelta = true;
986                      }
987                      frameTimeNanos = now - lastFrameOffset;
988                      mLastFrameTimeNanos = frameTimeNanos;
989                  }
990              }
991          }
992          try {
                  // 迭代执行队列所有任务
993              Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
994              for (CallbackRecord c = callbacks; c != null; c = c.next) {
995                  if (DEBUG_FRAMES) {
996                      Log.d(TAG, "RunCallback: type=" + callbackType
997                              + ", action=" + c.action + ", token=" + c.token
998                              + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
999                  }
1000                  c.run(frameTimeNanos);
1001              }
1002          } finally {
1003              synchronized (mLock) {
1004                  mCallbacksRunning = false;
1005                  do {
1006                      final CallbackRecord next = callbacks.next;
1007                      recycleCallbackLocked(callbacks);
1008                      callbacks = next;
1009                  } while (callbacks != null);
1010              }
1011              Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1012          }
1013      }
598      public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
599          if (callback == null) {
600              throw new IllegalArgumentException("callback must not be null");
601          }
602          
             //  也是调用 postCallbackDelayedInteral 方法
             // callbackTyoe 是 CALLBACK_ANIMATION 类型
             // token 是 FRAME_CALLBACK_TOKEN, action 是 FrameCallback 
603          postCallbackDelayedInternal(CALLBACK_ANIMATION,
604                  callback, FRAME_CALLBACK_TOKEN, delayMillis);
605      }
​
...
1198      private static final class CallbackRecord {
1199          public CallbackRecord next;
1200          public long dueTime;
1201          public Object action; // Runnable or FrameCallback
1202          public Object token;
1203  
1204          @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1205          public void run(long frameTimeNanos) {
1206              if (token == FRAME_CALLBACK_TOKEN) {
1207                  ((FrameCallback)action).doFrame(frameTimeNanos);
1208              } else {
1209                  ((Runnable)action).run();
1210              }
1211          }
1212      }
1213  

总结

  • Choreographer:使 CPU/GPU 的绘制在 VSYNC 到来时开始。 Choreographer 初始化时会创建一个表示对 Vsync 信号感兴趣的连接,当有绘制请求时通过 postCallback 方法请求下一次 Vsync 信号,当信号到来后才开始执行绘制任务。
  • 只有当 App 注册监听下一 Vsync 信号后才能接收到 Vsync 到来的回调。如果界面一直保持不变,那么 App 不会去接收每隔 16.6 ms一次的 Vsync 事件,但底层依旧会以这个频率来切换每一帧的画面(也是通过监听 Vsync 信号实现)。即当界面不变时屏幕也会固定每 16.6 ms 刷新,但 CPU/GPU 不会走绘制流程。
  • 当 View 请求刷新你时,这个任务并不会马上开始,而是需要等到下一个 Vsync 信号到来时才开始。 measure/layout/draw 流程运行完后,界面也不会立刻刷新,而会等到下一个 Vsync 信号到来时才进行缓存交换和显示。
  • 造成丢帧主要有两个原因:一是遍历绘制 View 树以及计算屏幕数据超过了16.6ms;二是主线程一直在处理其他耗时消息,导致绘制任务迟迟不能开始(同步屏障不能完全解决这个问题)。
  • 可通过Choreographer.getInstance().postFrameCallback()来监听帧率情况。

Choreographer 工作流程

image.png