基础知识 注:本文基于Android 10源码,为了文章的简洁性,引用源码的地方可能有所删减。
在一个典型的显示系统中,一般包括CPU、GPU、Display三个部分,CPU负责计算帧数据,把计算好的数据交给GPU,GPU会对图形数据进行渲染,渲染好后放到buffer(图像缓冲区)里存起来,然后Display(屏幕或显示器)负责把buffer里的数据呈现到屏幕上。
相关基础概念见Android图形系统综述 。
Choreographer :编舞者,指对CPU/GPU绘制的指导,收到VSync信号才开始绘制,保证绘制拥有完整的16.6ms。通常应用层不会直接使用Choreographer,而是使用更高级的API,如View.invalidate()等,可以通过Choreographer来监控应用的帧率。
入口: scheduleTraversals 从Android-Window机制原理 可知,在调用startActivity后,经过AMS的一些处理,后面又通过Binder调用目标进程的ActivityThread.handleResumeActivity方法,在这个方法里会回调目标Activity的onResume和makeVisible方法,在makeVisible方法里完成WindowManager.addView的过程,这个过程调用了ViewRootImpl.setView方法,内部又调用其scheduleTraversals方法,最后会走到performTraversals方法,接着就到了熟悉的measure,layout,draw三大流程了。
另外,查看View.invalidate方法的源码,也可以发现最后会调用到ViewRootImpl.scheduleTraversals方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 final ViewRootHandler mHandler = new ViewRootHandler();void scheduleTraversals () { if (!mTraversalScheduled) { mTraversalScheduled = true ; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null ); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } } final class TraversalRunnable implements Runnable { @Override public void run () { doTraversal(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable();void doTraversal () { if (mTraversalScheduled) { mTraversalScheduled = false ; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); performTraversals(); } }
首先使用mTraversalScheduled字段保证同时间多次更改只会刷新一次,然后为当前线程的MessageQueue添加同步屏障,来屏蔽同步消息,保证VSync到来后立即执行绘制,而不是要等前面的同步消息。调用mChoreographer.postCallback()方法发送了一个会在下一帧执行的回调,即在下一个VSync到来时会执行TraversalRunnable–>doTraversal()–>performTraversals()–>绘制流程。具体逻辑接着往下看。
Choreographer实例化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public ViewRootImpl (Context context, Display display) { mChoreographer = Choreographer.getInstance(); } public final class Choreographer { private static volatile Choreographer mMainInstance; private static final ThreadLocal<Choreographer> sThreadInstance = new ThreadLocal<Choreographer>() { @Override protected Choreographer initialValue () { Looper looper = Looper.myLooper(); if (looper == null ) { throw new IllegalStateException("The current thread must have a looper!" ); } Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP); if (looper == Looper.getMainLooper()) { mMainInstance = choreographer; } return choreographer; } }; public static Choreographer getInstance () { return sThreadInstance.get(); } }
可知Choreographer和Looper一样都是线程单例的,由ThreadLocal实现,见ThreadLocal原理 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public final class Choreographer { private static final boolean USE_VSYNC = SystemProperties.getBoolean("debug.choreographer.vsync" , true ); public static final int CALLBACK_COMMIT = 3 ; private static final int CALLBACK_LAST = CALLBACK_COMMIT; private long mLastFrameTimeNanos; private long mFrameIntervalNanos; private final FrameDisplayEventReceiver mDisplayEventReceiver; 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(); } setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1 )); } } private static float getRefreshRate () { DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo(Display.DEFAULT_DISPLAY); return di.getMode().getRefreshRate(); }
Choreographer中共有四种callbackType:
1 2 3 4 5 6 7 8 9 10 11 12 13 private static final String[] CALLBACK_TRACE_TITLES = {"input" , "animation" , "traversal" , "commit" };public static final int CALLBACK_INPUT = 0 ;public static final int CALLBACK_ANIMATION = 1 ;public static final int CALLBACK_TRAVERSAL = 2 ;public static final int CALLBACK_COMMIT = 3 ;private static final int CALLBACK_LAST = CALLBACK_COMMIT;
这四种类型的任务存入对应类型的CallbackQueue中,每当收到VSYNC信号时,Choreographer将按顺序处理这些类型的任务。
下面看看FrameHandler的源码,它用来处理异步消息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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 ; } } }
Vsync信号注册 DisplayEventReceiver mDisplayEventReceiver 是 FrameDisplayEventReceiver 类型的实例,在Choreographer构造方法中实例化,其父类为 DisplayEventReceiver。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public abstract class DisplayEventReceiver { public static final int VSYNC_SOURCE_APP = 0 ; private long mReceiverPtr; 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" ); } private static native long nativeInit (WeakReference<DisplayEventReceiver> receiver, MessageQueue messageQueue, int vsyncSource) ; }
nativeInit nativeInit是一个native方法,其实现在frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
中:
1 2 3 4 5 6 7 8 static jlong nativeInit (JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj, jint vsyncSource) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env, receiverWeak, messageQueue, vsyncSource); status_t status = receiver->initialize(); receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); return reinterpret_cast <jlong>(receiver.get()); }
NativeDisplayEventReceiver 继承自 DisplayEventDispatcher:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) { sp<ISurfaceComposer> sf (ComposerService::getComposerService()) ; if (sf != NULL ) { mEventConnection = sf->createDisplayEventConnection(vsyncSource); if (mEventConnection != NULL ) { mDataChannel = std ::make_unique<gui::BitTube>(); mEventConnection->stealReceiveChannel(mDataChannel.get()); } } } sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection ( ISurfaceComposer::VsyncSource vsyncSource) { if (vsyncSource == eVsyncSourceSurfaceFlinger) { return mSFEventThread->createEventConnection(); } else { return mEventThread->createEventConnection(); } }
从 Android-SurfaceFlinger启动与绘图原理 可以知道 EventThread.createEventConnection 创建了一个对 Vsync 信号感兴趣的连接。initialize 方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 status_t DisplayEventDispatcher::initialize () { status_t result = mReceiver.initCheck(); int rc = mLooper->addFd(mReceiver.getFd(), 0 , Looper::EVENT_INPUT, this , NULL ); if (rc < 0 ) { return UNKNOWN_ERROR; } return OK; } int DisplayEventReceiver::getFd () const { if (mDataChannel == NULL ) return NO_INIT; return mDataChannel->getFd(); }
mReceiver 是 DisplayEventReceiver 类型实例,位于frameworks/native/libs/gui/DisplayEventReceiver.cpp
。mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL)
用来监听 mReceiver 所获取的文件句柄,当有存在对 Vsync 信号感兴趣的连接 且接收到了 Vsync 信号 时,会发送数据到 mReceiver, 然后回调 DisplayEventDispatcher 中的 handleEvent 方法,具体源码参考 Android-SurfaceFlinger启动与绘图原理 中 addFd 的解析。
Looper.addFd BitTube
先看一下 BitTube, 这是 Android 提供的一种进程通讯方式,它通过 socketpair 来现全双工的通讯。在 BitTube 对象中有两个文件描述符: mReceiveFd 和 mSendFd, 以及 read 和 write 方法分别用来进行读写操作。获取两个文件描述符的函数:
1 2 3 4 5 6 7 int BitTube::getFd () const { return mReceiveFd; } 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)
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 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; 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 ); 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 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 private void scheduleFrameLocked (long now) { if (!mFrameScheduled) { mFrameScheduled = true ; if (USE_VSYNC) { if (isRunningOnLooperThreadLocked()) { scheduleVsyncLocked(); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true ); mHandler.sendMessageAtFrontOfQueue(msg); } } else { final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now); Message msg = mHandler.obtainMessage(MSG_DO_FRAME); msg.setAsynchronous(true ); mHandler.sendMessageAtTime(msg, nextFrameTime); } } } void doScheduleVsync () { synchronized (mLock) { if (mFrameScheduled) { scheduleVsyncLocked(); } } } private void scheduleVsyncLocked () { mDisplayEventReceiver.scheduleVsync(); }
DisplayEventReceiver.scheduleVsync 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public void scheduleVsync () { if (mReceiverPtr == 0 ) { } else { nativeScheduleVsync(mReceiverPtr); } } static void nativeScheduleVsync (JNIEnv* env, jclass clazz, jlong receiverPtr) { sp<NativeDisplayEventReceiver> receiver = reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr); status_t status = receiver->scheduleVsync(); } status_t DisplayEventDispatcher::scheduleVsync() { if (!mWaitingForVsync) { nsecs_t vsyncTimestamp; int32_t vsyncDisplayId; uint32_t vsyncCount; if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { } status_t status = mReceiver.requestNextVsync(); mWaitingForVsync = true ; } return OK; } status_t DisplayEventReceiver::requestNextVsync() { if (mEventConnection != NULL) { mEventConnection->requestNextVsync(); return NO_ERROR; } return NO_INIT; }
关于 requestNextVsync 的逻辑已经在 Android-SurfaceFlinger启动与绘图原理 中解析过了,它用来请求接收下一次 Vsync 信号,可以唤醒 EventThread 线程,等到 Vsync 信号到来后回调给 APP。
Vsync回调流程 DisplayEventDispatcher::handleEvent 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 int DisplayEventDispatcher::handleEvent(int , int events, void *) { nsecs_t vsyncTimestamp; int32_t vsyncDisplayId; uint32_t vsyncCount; if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { mWaitingForVsync = false ; dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount); } return 1 ; } void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) { JNIEnv* env = AndroidRuntime::getJNIEnv(); ScopedLocalRef<jobject> receiverObj (env, jniGetReferent(env, mReceiverWeakGlobal) ) ; if (receiverObj.get()) { env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count); } mMessageQueue->raiseAndClearException(env, "dispatchVsync" ); } int register_android_view_DisplayEventReceiver (JNIEnv* env) { jclass clazz = FindClassOrDie(env, "android/view/DisplayEventReceiver" ); gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); gDisplayEventReceiverClassInfo.dispatchVsync = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync" , "(JII)V" ); return res; }
由上可知,会调用到Java层 android/view/DisplayEventReceiver
的 dispatchVsync 方法:
1 2 3 4 5 6 7 8 public abstract class DisplayEventReceiver { private void dispatchVsync (long timestampNanos, int builtInDisplayId, int frame) { onVsync(timestampNanos, builtInDisplayId, frame); } public void onVsync (long timestampNanos, int builtInDisplayId, int frame) { } }
FrameDisplayEventReceiver.onVsync 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { private boolean mHavePendingVsync; private long mTimestampNanos; private int mFrame; @Override public void onVsync (long timestampNanos, int builtInDisplayId, int frame) { if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { Log.d(TAG, "Received vsync from secondary display, but we don't support " + "this case yet. Choreographer needs a way to explicitly request " + "vsync for a specific display to ensure it doesn't lose track " + "of its scheduled vsync." ); scheduleVsync(); return ; } long now = System.nanoTime(); if (timestampNanos > now) { 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); } }
Choreographer.doFrame 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt("debug.choreographer.skipwarning" , 30 );void doFrame (long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { if (!mFrameScheduled) { return ; } 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; frameTimeNanos = startNanos - lastFrameOffset; } if (frameTimeNanos < mLastFrameTimeNanos) { 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 { AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS); mFrameInfo.markInputHandlingStart(); doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); mFrameInfo.markAnimationsStart(); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); mFrameInfo.markPerformTraversalsStart(); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); } finally { AnimationUtils.unlockAnimationClock(); } }
Choreographer.doCallbacks 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 void doCallbacks (int callbackType, long frameTimeNanos) { CallbackRecord callbacks; synchronized (mLock) { final long now = System.nanoTime(); 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; frameTimeNanos = now - lastFrameOffset; mLastFrameTimeNanos = frameTimeNanos; } } } try { for (CallbackRecord c = callbacks; c != null ; c = c.next) { c.run(frameTimeNanos); } } finally { synchronized (mLock) { mCallbacksRunning = false ; do { final CallbackRecord next = callbacks.next; recycleCallbackLocked(callbacks); callbacks = next; } while (callbacks != null ); } } } private static final class CallbackRecord { public CallbackRecord next; public long dueTime; public Object action; public Object token; public void run (long frameTimeNanos) { if (token == FRAME_CALLBACK_TOKEN) { ((FrameCallback)action).doFrame(frameTimeNanos); } else { ((Runnable)action).run(); } } }
Choreographer.postFrameCallback 用法 postFrameCallback方法通常用来计算丢帧情况,用法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public void onCreate () { super .onCreate(); 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 ){ Log.i(TAG, "Skipped " + skippedFrames + " frames! " + "The application may be doing too much work on its main thread." ); } } mLastFrameTimeNanos = frameTimeNanos; Choreographer.getInstance().postFrameCallback(this ); } }
源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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, callback, FRAME_CALLBACK_TOKEN, delayMillis); } public interface FrameCallback { void doFrame (long frameTimeNanos) ; }
总结
Choreographer: 使 CPU/GPU 的绘制是在 VSYNC 到来时开始。Choreographer 初始化时会创建一个表示对 Vsync 信号感兴趣的连接,当有绘制请求时通过 postCallback 方法请求下一次 Vsync 信号,当信号到来后才开始执行绘制任务。
只有当 App 注册监听下一个 Vsync 信号后才能接收到 Vsync 到来的回调。如果界面一直保持不变,那么 App 不会去接收每隔 16.6ms 一次的 Vsync 事件,但底层依旧会以这个频率来切换每一帧的画面(也是通过监听 Vsync 信号实现)。即当界面不变时屏幕也会固定每 16.6ms 刷新,但 CPU/GPU 不走绘制流程。
当 View 请求刷新时,这个任务并不会马上开始,而是需要等到下一个 Vsync 信号到来时才开始;measure/layout/draw 流程运行完后,界面也不会立刻刷新,而会等到下一个 VSync 信号到来时才进行缓存交换和显示。
造成丢帧主要有两个原因:一是遍历绘制 View 树以及计算屏幕数据超过了16.6ms;二是主线程一直在处理其他耗时消息,导致绘制任务迟迟不能开始(同步屏障不能完全解决这个问题)。
可通过Choreographer.getInstance().postFrameCallback()来监听帧率情况。
阅读这篇文章建议先阅读 SurfaceFlinger 启动与工作流程 这篇文章,然后结合 Choreographer 的工作流程,可以对 Vsync 信号是怎么协调 App 端的绘制任务以及 SurfaceFlinger 的合成任务有一个比较清晰的认识。
用一张图总结一下 Choreographer 的工作流程: