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

  本文主要探討可以觸發performTraversals()執行的invalidate()、postInvalidate()和requestLayout()方法的流程。在調用這三個方法到最後執行到performTraversals()方法,涉及到到經過Choroegrapher請求Vsync信號,實現按幀繪製的流程,因此還會介紹Choroegrapher類的工做流程。數組

1、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()方法,進入該代碼。app

    @Override
    public void requestLayout() {
     //該boolean變量會在ViewRootImpl.performLayout()開始時置爲ture,結束置false
//表示當前不處於Layout過程
     if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }

  若是當前不是正在執行layout過程,則會調用scheduleTraversals()方法,進入ViewRootImpl.scheduleTraversals()。ide

    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()方法。oop

    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        
       ... //用於調試相關代碼
performTraversals(); ... //用於調試相關代碼 } }

  而後調用到ViewRootImpl.performTraversals()方法。佈局

 

2、invalidate()與postInvalidate()流程 post

  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()方法,進入該代碼。this

    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()方法。

 

3、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類的分析。

相關文章
相關標籤/搜索