本文主要探討能夠觸發performTraversals()執行的invalidate()、postInvalidate()和requestLayout()方法的流程。在調用這三個方法到最后執行到performTraversals()方法,涉及到到通過Choroegrapher請求Vsync信號,實現按幀繪制的流程,所以還會介紹Choroegrapher類的工作流程。
一、requestLayout()流程
invalidate()和postInvalidate()能夠觸發View的重畫,這兩個方法最終會調用到performTraversals()中的performDraw()來完成重繪制,但是是否會執行onMeasure()和onLayout()過程要根據標志位的狀況來決定;requesetLayout()方法也會調用到performTraversals()方法,但是只會執行measure和layout流程,不會調用到draw流程來觸發重畫動作。直接來看View.requestLayout()代碼。
@CallSuper public void requestLayout() { if (mMeasureCache != null) mMeasureCache.clear();
//如果當前的整個View樹在進行布局流程的話,則會調用requestLayoutDuringLayout()
//讓這次的布局延時執行 if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) { // Only trigger request-during-layout logic if this is the view requesting it, // not the views in its parent hierarchy ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null && viewRoot.isInLayout()) { if (!viewRoot.requestLayoutDuringLayout(this)) { return; } } mAttachInfo.mViewRequestingLayout = this; }
//PFLAG_FORCE_LAYOUT會在執行View的measure()和layout()方法時判斷
//只有設置過該標志位,才會執行measure()和layout()流程 mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); } if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) { mAttachInfo.mViewRequestingLayout = null; } }
該方法主要是設置了PFLAG_FORCE_LAYOUT和PFLAG_INVALIDATED到當前View的Flag中,然后調用到當前View(當前View可能是一個控件View,也可能是一個布局View,因為對於這兩類View都能調用requestLayout()方法)的父布局View的requestLayout()方法,父布局View是ViewGroup類型,沒有重寫該requestLayout()方法,所以實際還是調回到View.requestLayout()方法的這套邏輯。這個過程,就是設置當前View標志位后,就不斷的向上調用父布局View的requestLayout(),最后調用到根View即DecorView的requestLayout(),而DecorView的mParent變量指向的是當前窗口對應的ViewRootImpl對象,最后一次設置完DecorView標志位后,調用到ViewRootImpl.requestLayout()方法,進入該代碼。
@Override public void requestLayout() {
//該boolean變量會在ViewRootImpl.performLayout()開始時置為ture,結束置false
//表示當前不處於Layout過程
if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
如果當前不是正在執行layout過程,則會調用scheduleTraversals()方法,進入ViewRootImpl.scheduleTraversals()。
void scheduleTraversals() { if (!mTraversalScheduled) {
//在下一段代碼處會置回false
//表示在排好這次繪制請求前,不再排其它的繪制請求 mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }
這里主要是調用到了ViewRootImpl的另一個重要的變量mChoreographer,它是Choreographer類型的,這個對象會請求Vsync信號來控制繪制的進行,實現了按幀進行繪制的機制,這個類會在后文進行介紹。該方法對於繪制的請求經過了Choreographer的編排后,最終會調用回ViewRootImpl.doTraversal()方法。
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
... //用於調試相關代碼 performTraversals(); ... //用於調試相關代碼 } }
然后調用到ViewRootImpl.performTraversals()方法。
二、invalidate()與postInvalidate()流程
invalidate()與postInvalidate()都是用於被調用來觸發View的更新(重畫)動作,區別在於invalidate()方法是在UI線程自身中使用,而postInvalidate()是非UI線程中使用。 首先來看View.postInvalidate()。
public void postInvalidate() { postInvalidateDelayed(0); } public void postInvalidateDelayed(long delayMilliseconds) { // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); } }
調用到了對應的ViewRootImpl對象的dispatchInvalidateDelayed()方法,進入該代碼。
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) { Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); mHandler.sendMessageDelayed(msg, delayMilliseconds); }
這里實現了一個消息機制,發送了MSG_INVSLIDSTE。進入處理消息的ViewRootImpl.handleMessage()方法。
@Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_INVALIDATE: ((View) msg.obj).invalidate(); break; ... }
這里實際上就是調回了調用postInvalidate()方法的View的invalidate()方法。由於invalidate()方法只能在UI線程執行,所以postInvalidate只是實現了一個消息機制,讓用戶能夠在非UI線程使用,最終還是調用到invalidate()方法來觸發重畫,實現界面更新動作。繼續來看View.invalidate()方法,該方法邏輯的實際實際上時調用到invalidateInternal()方法來實現的。
public void invalidate() { invalidate(true); } void invalidate(boolean invalidateCache) {
//mLeft、mRigth、mTop、mBottom記錄的是當前View邊界距離其父布局View邊界的距離 invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); } void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) { if (mGhostView != null) { mGhostView.invalidate(true); return; }
//如果當前視圖為不可見狀態且沒有動畫正在執行,且其父布局也沒有過渡動畫執行,則跳過 if (skipInvalidate()) { return; }
//當前View沒有正在執行該方法
//或繪制緩存可用或未重繪過或透明度發生改變
//PFLAG_DRAWN會在該方法內去改標志位
//PFLAG_INVALIDATED會在View.draw()方法執行時去掉該標志位
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
//如果需要全部重繪,invalidate()未傳參調用時默認為true
if (fullInvalidate) { mLastIsOpaque = isOpaque(); mPrivateFlags &= ~PFLAG_DRAWN; } mPrivateFlags |= PFLAG_DIRTY; if (invalidateCache) { mPrivateFlags |= PFLAG_INVALIDATED; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; } // Propagate the damage rectangle to the parent view.
//damage記錄的區域是需要更新的dirty區域,當前的坐標時相對於自身來設置的
//通過不斷調用到父類的invalidateChild()方法,來不斷更新dirty區域的相對坐標
final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; if (p != null && ai != null && l < r && t < b) { final Rect damage = ai.mTmpInvalRect; damage.set(l, t, r, b); p.invalidateChild(this, damage); } // Damage the entire projection receiver, if necessary. if (mBackground != null && mBackground.isProjected()) { final View receiver = getProjectionReceiver(); if (receiver != null) { receiver.damageInParent(); } } // Damage the entire IsolatedZVolume receiving this view's shadow. if (isHardwareAccelerated() && getZ() != 0) { damageShadowReceiver(); } } }
這里會通過調用mParent的invalidateChild()方法,來觸發父類對於dirty區域的調整(可能會調整可能還是原區域)及改區域相對坐標的調整。進入ViewGroup.invalidateChild()方法。
@Override public final void invalidateChild(View child, final Rect dirty) { ViewParent parent = this; final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { // If the child is drawing an animation, we want to copy this flag onto // ourselves and the parent to make sure the invalidate request goes // through
//drawAnimation記錄調用該方法的子View是否正在執行動畫 final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) == PFLAG_DRAW_ANIMATION; // Check whether the child that requests the invalidate is fully opaque // Views being animated or transformed are not considered opaque because we may // be invalidating their old position and need the parent to paint behind them.
//調用該方法的子View是否不透明:處於不透明狀態且沒有在執行動畫且變化矩陣沒有變化
//Matrix可以用於View的平移、縮放、擴放、旋轉等操作,比如某些應用上的雙指縮放功能 Matrix childMatrix = child.getMatrix(); final boolean isOpaque = child.isOpaque() && !drawAnimation && child.getAnimation() == null && childMatrix.isIdentity(); // Mark the child as dirty, using the appropriate flag // Make sure we do not set both flags at the same time int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY; if (child.mLayerType != LAYER_TYPE_NONE) { mPrivateFlags |= PFLAG_INVALIDATED; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; }
final int[] location = attachInfo.mInvalidateChildLocation;
//記錄子View邊界距離父View左邊界和上邊界的距離到Location中,用於下一段代碼中的計算 location[CHILD_LEFT_INDEX] = child.mLeft; location[CHILD_TOP_INDEX] = child.mTop;
//如果子View設置了變換矩陣,則根據變換矩陣調整dirty區域 if (!childMatrix.isIdentity() || (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { RectF boundingRect = attachInfo.mTmpTransformRect; boundingRect.set(dirty); Matrix transformMatrix; if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { Transformation t = attachInfo.mTmpTransformation; boolean transformed = getChildStaticTransformation(child, t); if (transformed) { transformMatrix = attachInfo.mTmpMatrix; transformMatrix.set(t.getMatrix()); if (!childMatrix.isIdentity()) { transformMatrix.preConcat(childMatrix); } } else { transformMatrix = childMatrix; } } else { transformMatrix = childMatrix; } transformMatrix.mapRect(boundingRect); dirty.set((int) Math.floor(boundingRect.left), (int) Math.floor(boundingRect.top), (int) Math.ceil(boundingRect.right), (int) Math.ceil(boundingRect.bottom)); }
//這是一個從當前的布局View向上不斷遍歷當前布局View的父布局,最后遍歷到ViewRootImpl的循環 do { View view = null;
//parent可能為ViewGroup類型,也可能為ViewRootImpl類型
//最后一次循環執行時為ViewRootImpl類型 if (parent instanceof View) { view = (View) parent; }
//如果子View正在執行動畫,設置遍歷的父布局View的動畫標識 if (drawAnimation) { if (view != null) { view.mPrivateFlags |= PFLAG_DRAW_ANIMATION; } else if (parent instanceof ViewRootImpl) { ((ViewRootImpl) parent).mIsAnimating = true; } } // If the parent is dirty opaque or not dirty, mark it dirty with the opaque // flag coming from the child that initiated the invalidate
//設置當前ViewGroup的Dirty標識,表示當前的ViewGroup需要重繪 if (view != null) { if ((view.mViewFlags & FADING_EDGE_MASK) != 0 && view.getSolidColor() == 0) { opaqueFlag = PFLAG_DIRTY; } if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) { view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag; } }
//調用當前布局View的invalidateChildParent()方法,返回的值為當前布局View的父布局
//通過循環向上調用,最后返回的根布局是ViewRootImpl對象
parent = parent.invalidateChildInParent(location, dirty); if (view != null) { // Account for transform on current parent Matrix m = view.getMatrix(); if (!m.isIdentity()) { RectF boundingRect = attachInfo.mTmpTransformRect; boundingRect.set(dirty); m.mapRect(boundingRect); dirty.set((int) Math.floor(boundingRect.left), (int) Math.floor(boundingRect.top), (int) Math.ceil(boundingRect.right), (int) Math.ceil(boundingRect.bottom)); } } } while (parent != null); } }
在do-while循環中會調用到parent = parent.invalidateChildInParent(location, dirty),這里執行到ViewGroup.invalidateChildInParent()方法。
@Override public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
// if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN || (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
//如果ViewGroup有沒有動畫執行或者動畫已經完成 if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) != FLAG_OPTIMIZE_INVALIDATE) {
//dirty記錄的是最開始調到invalidate()的View的區域
//dirty的四個坐標值值在執行下面代碼是相對於當前循環到上一個ViewGroup來確定的
//這里做了一個偏移動作,偏移的量是當前上一個ViewGroup相對於現在ViewGroup的偏移值
//做完下面的偏移操作后,dirty的四個坐標就是想對於當前ViewGroup的坐標值了 dirty.offset([CHILD_LEFT_INDEX] - mScrollX, location[CHILD_TOP_INDEX] - mScrollY);
//如果當前ViewGroup需要裁剪View
//則將當前ViewGroup的區域與View的區域做求並集的操作 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) { dirty.union(0, 0, mRight - mLeft, mBottom - mTop); } final int left = mLeft; final int top = mTop;
//如果當前ViewGroup需要裁剪View,且ViewGroup區域與View區域沒有並集,則dirty置空 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) { dirty.setEmpty(); } } mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
//用於循環到下一個ViewGroup時做offset操作 location[CHILD_LEFT_INDEX] = left; location[CHILD_TOP_INDEX] = top; if (mLayerType != LAYER_TYPE_NONE) { mPrivateFlags |= PFLAG_INVALIDATED; } return mParent; } else {//如果當前ViewGroup中有動畫要執行 mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID; location[CHILD_LEFT_INDEX] = mLeft; location[CHILD_TOP_INDEX] = mTop;
//如果需要對子View裁剪則設置dirty為當前ViewGroup區域
//如果不需要則求當前ViewGroup區域與原ditry區域並集
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { dirty.set(0, 0, mRight - mLeft, mBottom - mTop); } else { // in case the dirty rect extends outside the bounds of this container dirty.union(0, 0, mRight - mLeft, mBottom - mTop); } if (mLayerType != LAYER_TYPE_NONE) { mPrivateFlags |= PFLAG_INVALIDATED; } return mParent; } } return null; }
invalidateChildInParent()主要是完成了dirty區域在調用該方法的ViewGroup中的更新,dirty指示的區域就是需要重繪制的區域。如果ViewGroup沒有動畫在執行,則dirty區域還是原來的區域,只需要通過偏移操作更改該區域的坐標值從相對於上一個ViewGroup(父ViewGroup),到相對於當前ViewGroup;如果有動畫要執行,則表示當前整個ViewGroup都需要重繪,更改dirty值為當前ViewGroup 區域。
do-while最后一次循環最后會調用到ViewRootImpl.invalidateChildInParent()方法,進入該代碼。
@Override public ViewParent invalidateChildInParent(int[] location, Rect dirty) { checkThread(); if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
//如果傳入一個null drity,則表示要重繪當前ViewRootImpl指示的整個區域
//如果傳入一個empty dirty,則表示經過計算需要重繪的區域不需要繪制
if (dirty == null) { invalidate(); return null; } else if (dirty.isEmpty() && !mIsAnimating) { return null; }
... invalidateRectOnScreen(dirty); return null; }
調用到了ViewRootImpl.invalidateRectOnScreen()方法,進入該代碼。
private void invalidateRectOnScreen(Rect dirty) {
//mDirty記錄的是當前ViewRootImpl里還未進行重繪需要重繪的區域
//mDirty會在ViewRootImpl.draw()方法結尾處設置為empty
final Rect localDirty = mDirty; if (!localDirty.isEmpty() && !localDirty.contains(dirty)) { mAttachInfo.mSetIgnoreDirtyState = true; mAttachInfo.mIgnoreDirtyState = true; } // Add the new dirty rect to the current one
//當前已有的dirty區域與此次dirty區域做並集 localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom); // Intersect with the bounds of the window to skip // updates that lie outside of the visible region final float appScale = mAttachInfo.mApplicationScale;
//處理窗口縮放與做完並集的localDirty做交集 final boolean intersected = localDirty.intersect(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); //如果沒有交集
if (!intersected) { localDirty.setEmpty(); }
//mWillDrawSoon在performTraversals()方法開始時置為true,結束時置false
//如果沒有在執行performTraversals &&(intersected || 正在執行動畫) if (!mWillDrawSoon && (intersected || mIsAnimating)) { scheduleTraversals(); } }
最后會調用到scheduleTraversals()方法,后續在請求到Vsync信號后,便會調用到peformTraversals()方法。
三、Choreographer類分析
“編舞類”Choreoprapher的作用是編排輸入事件、動畫事件和繪制事件的執行,通過調用Choreoprapher.postCallback()方法,向Choreoprapher加入需要編排的事件,而Choreoprapher則通過請求Vsync信號,來控制這些事件按照屏幕刷新周期有規律的執行,即是實現了按幀繪制的機制。
在ViewRootImpl中,會調用mChoreographer = Choreographer.getInstance()來初始化一個Choreographer變量。進入Choreographer.getInstance()代碼。
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!"); } return new Choreographer(looper); } }; public static Choreographer getInstance() { return sThreadInstance.get(); }
這里實際調用了ThreadLocal類型的靜態常量的get()方法,ThreadLocal中保存的類型是Choreographer類。根據ThreadLocal機制,sThreadInstance.get()方法會調用到上面代碼中實現的initialValue()方法,該方法返回一個Choregrapher類型對象,返回的該對象即作為getInstance()方法的返回,也是最后賦值給了ViewRootImpl中的mChoreogropher變量。在initialValue()方法中會new一個Choreographer對象,進入構建方法。
private Choreographer(Looper looper) { //調用該方法的源頭是UI線程,所有looper為UI線程的looper mLooper = looper; mHandler = new FrameHandler(looper); //如果系統使用Vsync機制,則創建一個Vsync信號的接收器FrameDisplayEventReceiver類 mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null; mLastFrameTimeNanos = Long.MIN_VALUE; mFrameIntervalNanos = (long)(1000000000 / getRefreshRate()); //創建回調數組,CALLBAKCK_LAST=3,后文詳解 mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; for (int i = 0; i <= CALLBACK_LAST; i++) { mCallbackQueues[i] = new CallbackQueue(); } }
首先來說mCallbackQueues,這是一個長度為4的CallbackQueue類型的數組,即保存了四個回調隊列。每個回調隊列能夠保存多個CallbackRecord,即是回調事件。這四個隊列分別保存四類回調事件:Input事件、Animation事件、Draw事件,還有一種是用來解決動畫啟動問題的事件。在ViewRootImpl.scheduleTraversals()方法中,便會調用相關方法向隊列中添加一個Draw事件,並觸發后續到請求信號來處理事件的動作。
void scheduleTraversals() { ... mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); ... }
繼續來看Choreographer.postCallback()方法,該方法是調用到postCallbackDelayedInternal()方法來實現主要邏輯。
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) { ... //異常情況判斷 postCallbackDelayedInternal(callbackType, action, token, delayMillis); } private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { ... // Debug log 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); } } }
調用addCallbackLock()方法,會根據本次事件信息生成一個CallbackRecord,添加到隊列中,但並不一定添加在隊列到尾部。隊列中所有事件的排列是按照dueTime的值由小到大排列大,即越快要求執行的事件排列得越前,所以在添加事件到隊列時會根據dueTime插入到對應的位置。
插入隊列操作完成后,會調用scheduleFrameLoacked()方法。
private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled = true; if (USE_VSYNC) { //如果使用了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. //如果前線程開啟了Looper,則調用scheduleVsyncLocked()請求Vsync信號 if (isRunningOnLooperThreadLocked()) { scheduleVsyncLocked(); } else {//如果當前線程未啟動Looper //則發消息到調用創建Choreographer的線程來請求Vsync信號 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); } } else {//如果未使用Vsync機制,則手動計算下一次繪制時間,使用延時消息來控制 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機制的,且scheduleFrameLocked()也是被UI線程調用執行的,所以直接調用到Choreographer.scheduleVsyncLocked()方法,進入該代碼。
private void scheduleVsyncLocked() { mDisplayEventReceiver.scheduleVsync(); }
這里直接調用到mDisplayEventReceiver的scheduleVsync()方法,該變量是FrameDisplayEventReceiver類型的,該類繼承自DisplayEventReceiver類。scheduleVsync()相當於發起了一次Vsync請求,這樣在請求之后下一個Vsync信號發出時,FrameDisplayEventReceiver類便能接收到這詞Vsync信號,會調用到FrameDisplayEventReceiver類的onVsync()方法,在onVsync()方法中會發送消息到UI線程,調用到doFrame()方法,Frame是幀的意思,doFrame則表示這次接收到Vsync信號的這一幀內要做的事,進入FrameDisplayEventReceiver.doFrame()方法(FrameDisplayEventReceiver類時Choreographer內部類),
void doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) {
//該變量會在scheduleFrameLocked()方法開始時設置為true,本方法結束置為false
//表示有callback事件需要安排執行 if (!mFrameScheduled) { return; // no work to do } if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) { mDebugPrintNextFrameTimeDelta = false; Log.d(TAG, "Frame time delta: " + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms"); }
//frameTimeNanos表示Vsync信號發出的時間或者幀開始的時間 long intendedFrameTimeNanos = frameTimeNanos;
//當前時間 startNanos = System.nanoTime(); final long jitterNanos = startNanos - frameTimeNanos;
//當前時間距離Vsync信號時間超過了屏幕的刷新周期,即一幀16ms的時間 if (jitterNanos >= mFrameIntervalNanos) { final long skippedFrames = jitterNanos / mFrameIntervalNanos;
//如果超過太多,即跳過了太多幀,則打出Log提示跳過了太多幀,可能是主線程做了太多事了 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; }
//如果距離最后一幀時間未超過屏幕刷新周期,則重新請求Vsync信號 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; } mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos); mFrameScheduled = false;
//設置本次幀的執行時間為最后一次的幀執行時間 mLastFrameTimeNanos = frameTimeNanos; } try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame"); AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
//依次從隊列中取出這四類事件進行執行
//但不一定都會執行這四類事件,要看隊列中是否有post過且符合這一幀執行到條件的事件 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(); 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方法來依次執行當前時間對應的四類事件。由於CALLBACK_COMMIT是一種修正屬性動畫啟動事件過長導致掉幀問題的一種機制,並不是真正會執行在主線程的流程,這里不做詳解。所以在執行事件時,主要是依次執行了input、animation和traversal事件。我們可以抓一個systrace來直觀的了解這個過程,以UC瀏覽器雙指擴放頁面的繪制過程中的某一幀為例。

doFrame()方法中首先執行來input事件的處理,然后后面有個很短的矩形體條,執行的是animation事件;之后便是執行到了traversal事件,在執行traversal流程中執行了draw流程,但並沒有執行measure和layout流程,因為本次繪制不需要重新測量和布局;在執行draw流程過程中實際調用到了View的draw()方法。
繼續來看Choroegrapher.doCallbacks()方法的實現。
void doCallbacks(int callbackType, long frameTimeNanos) { CallbackRecord callbacks; synchronized (mLock) { // We use "now" to determine when callbacks become due because it's possible // for earlier processing phases in a frame to post callbacks that should run // in a following phase, such as an input event that causes an animation to start. final long now = System.nanoTime();
//根據幀開始的時間,取出當前該類型隊列中的一個callback事件 callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS); if (callbacks == null) { return; } mCallbacksRunning = true; ... //CALLBACK_COMMIT事件的處理try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]); for (CallbackRecord c = callbacks; c != null; c = c.next) { if (DEBUG_FRAMES) { Log.d(TAG, "RunCallback: type=" + callbackType + ", action=" + c.action + ", token=" + c.token + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime)); } c.run(frameTimeNanos); } } finally { synchronized (mLock) { mCallbacksRunning = false; do { final CallbackRecord next = callbacks.next; recycleCallbackLocked(callbacks); callbacks = next; } while (callbacks != null); } Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
首先來看下CallbackQueue.extractDueCallbacksLocked()方法,了解隊列取事件執行的機制。
public CallbackRecord extractDueCallbacksLocked(long now) {
//返回隊列頭事件,即要求最快要執行的事件 CallbackRecord callbacks = mHead; if (callbacks == null || callbacks.dueTime > now) { return null; }
//把頭回調事件后面所有執行時間已經到了事件全部舍棄
CallbackRecord last = callbacks; CallbackRecord next = last.next; while (next != null) { if (next.dueTime > now) { last.next = null; break; } last = next; next = next.next; }
//next表示的是未到執行時間且要求執行到時間最早的事件 mHead = next; return callbacks; }
取出當前幀需要執行的回調事件后,便會執行到該事件的run()方法,在使用這里會調用到CallbackRecord的run()方法。
private static final class CallbackRecord { public CallbackRecord next; public long dueTime; public Object action; // Runnable or FrameCallback public Object token; public void run(long frameTimeNanos) { if (token == FRAME_CALLBACK_TOKEN) { ((FrameCallback)action).doFrame(frameTimeNanos); } else { ((Runnable)action).run(); } } }
回想我們在ViewRootImpl中調用postCallback()方法的三個參數值,第一個事件類型為Choreographer.CALLBACK_TRAVERSAL,表示是繪制事件,用於指示該事件放入對應隊列,第二個則是一個TraversalRunnable類型的Runnable,則賦值給了這里的action,第三個是null,所以上面代碼的run()方法,實際執行到了TraversalRunnable的run()方法。
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } }
該方法則調用到了doTraversal()方法,后續則調用到了ViewRootImpl.performTraversals()方法。由於run在了UI線程,所以后續到繪制動作也是在UI線程執行到。至此完成了Choroegrapher類的分析。
