源碼分析篇 - Android繪制流程(三)requestLayout()與invalidate()流程及Choroegrapher類分析


  本文主要探討能夠觸發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類的分析。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM