invalidate方法源码追踪

 首先调用

public void invalidate() {

        invalidate(true);

 }

调用invalidete 带参数的方法

 void invalidate(boolean invalidateCache) {

        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);

}

调用invalidateInternal方法

 void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,

            boolean fullInvalidate) {

        if (mGhostView != null) {

            mGhostView.invalidate(true);

            return;

        }

        if (skipInvalidate()) {

            return;

        }

        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)) {

            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.

            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();

            }

        }

    }

invalidateInternal里面调用了ViewParentinvaludateChild方法,而ViewParent就是当前View的外层ViewGroup

查看ViewGroupinvaludateChild方法.这个方法里面有个while循环,一直向外层ViewGroup回调invaludateChildInParent方法。我们都知道最终顶层的View就是DecoView,DecoView里面的ViewParent又是哪个呢?

 public final void invalidateChild(View child, final Rect dirty) {

        ViewParent parent = this;

        final AttachInfo attachInfo = mAttachInfo;

       //省略部分代码

//while循环,一直向上回调ViewGroup invalidateChildInParent

            do {

                View view = null;

                if (parent instanceof View) {

                    view = (View) parent;

                }

          

                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) (boundingRect.left - 0.5f),

                                (int) (boundingRect.top - 0.5f),

                                (int) (boundingRect.right + 0.5f),

                                (int) (boundingRect.bottom + 0.5f));

                    }

                }

            } while (parent != null);

        }

    }

WindowManagerGlobal.java viewDecorView

当我们setContent方法后,在Activity onResume方法前,会把当前的顶层DecoView添加到addView WindowManager里面,而WindowManager的实现类WindowManagerImpl里面的调用的是WindowManagerGlobaladdView方法,如下。看注释的关系代码,这里实例化了一个ViewRootImpl,调用了setView方法,传入的view参数就是DecorView

 public void addView(View view, ViewGroup.LayoutParams params,

            Display display, Window parentWindow)

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;

//关键代码

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);

            mRoots.add(root);

            mParams.add(wparams);

        }

        // do this last because it fires off messages to start doing things

        try {

            root.setView(view, wparams, panelParentView);

        } catch (RuntimeException e) {

            // BadTokenException or InvalidDisplayException, clean up.

            synchronized (mLock) {

                final int index = findViewLocked(view, false);

                if (index >= 0) {

                    removeViewLocked(index, true);

                }

            }

            throw e;

        }

    }

ViewRootImpl setView方法,其中viewDecorView,在该方法里面调用了view.assigenParent(this),ViewRootImpl设置为DecorViewViewParent.

 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

        synchronized (this) {

            if (mView == null) {

                mView = view;

                mAttachInfo.mDisplayState = mDisplay.getState();

                mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);

                CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();

                mTranslator = compatibilityInfo.getTranslator();

                mAdded = true;

                int res; /* = WindowManagerImpl.ADD_OKAY; */

                // Schedule the first layout -before- adding to the window

                // manager, to make sure we do the relayout before receiving

                // any other events from the system.

                requestLayout();

               

//关键代码.设置DecorViewviewParentViewRootImpl

                view.assignParent(this);

                mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;

                mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;

                if (mAccessibilityManager.isEnabled()) {

                    mAccessibilityInteractionConnectionManager.ensureConnection();

                }

                if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {

                    view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);

                }

             

        }

    }

ViewGroup invalidateChild方法里面的while循环完最终调用ViewRootImpl里面的invaludateChild方法,查看ViewRootImpl里面的invalidateChild方法

ViewRootImpl

public void invalidateChild(View child, Rect dirty) {

        invalidateChildInParent(null, dirty);

    }

调用了invalidateChildInParent方法

 @Override

    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {

        checkThread();

        if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);

        if (dirty == null) {

            invalidate();

            return null;

        } else if (dirty.isEmpty() && !mIsAnimating) {

            return null;

        }

        if (mCurScrollY != 0 || mTranslator != null) {

            mTempRect.set(dirty);

            dirty = mTempRect;

            if (mCurScrollY != 0) {

                dirty.offset(0, -mCurScrollY);

            }

            if (mTranslator != null) {

                mTranslator.translateRectInAppWindowToScreen(dirty);

            }

            if (mAttachInfo.mScalingRequired) {

                dirty.inset(-1, -1);

            }

        }

        invalidateRectOnScreen(dirty);

        return null;

    }

调用了invalidateRectOnScreen方法,由方法名可猜测该方法是刷新屏幕上面的一个Rect区域,而Rect区域就是调用invalidate方法的那个View大小

 private void invalidateRectOnScreen(Rect dirty) {

        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

        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;

        final boolean intersected = localDirty.intersect(0, 0,

                (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));

        if (!intersected) {

            localDirty.setEmpty();

        }

        if (!mWillDrawSoon && (intersected || mIsAnimating)) {

            scheduleTraversals();

        }

    }

里面调用了scheduleTraversals方法,

scheduleTraversals方法里面发送了一个消息,将执行TraversalRunnable 任务方法

void scheduleTraversals() {

        if (!mTraversalScheduled) {

            mTraversalScheduled = true;

            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

            mChoreographer.postCallback(

                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

            if (!mUnbufferedInputDispatch) {

                scheduleConsumeBatchedInput();

            }

            notifyRendererOfFramePending();

            pokeDrawLockIfNeeded();

        }

    }

TraversalRunnable 任务方法

 final class TraversalRunnable implements Runnable {

        @Override

        public void run() {

            doTraversal();

        }

    }

这个方法又调用了performTraversals()

void doTraversal() {

        if (mTraversalScheduled) {

            mTraversalScheduled = false;

            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {

                Debug.startMethodTracing("ViewAncestor");

            }

            performTraversals();

            if (mProfile) {

                Debug.stopMethodTracing();

                mProfile = false;

            }

        }

    }

performTraversals这个方法挺长的,里面执行的方法也多。继续看

里面依次可能会调用了performMeasure,performLayoutperformDraw。这三个和我们常用的onMeasure,onLayout,onDraw方法就很像了,因为这三个方法最终也会调用我们常用的onXXX方法。在这里这三个方法不一定都会调用,当我们调用invalidate的时候,也就是说我们只想调用绘制我们的View的方法,这个时候只会调用到performDraw方法,当我们的view如果位置发生改变了,则也会调用到performLayout方法,如果大小也改变了,则也会调用perforMeasure方法。这三个方法就会回调我们自己View里面的mesure,layout,draw方法,measure内部会回调onMeasure,layout内部会回调onLayout,draw内部会回调onDraw。追踪到这里就差不多了,因为最终会回调到那个invalidate方法的viewonDraw方法里去。

 private void performTraversals() {

        // cache mView since it is used so much below...

        final View host = mView;

        mIsInTraversal = true;

        mWillDrawSoon = true;

        boolean windowSizeMayChange = false;

        boolean newSurface = false;

        boolean surfaceChanged = false;

        WindowManager.LayoutParams lp = mWindowAttributes;

        int desiredWindowWidth;

        int desiredWindowHeight;

        final int viewVisibility = getHostVisibility();

        boolean viewVisibilityChanged = mViewVisibility != viewVisibility

                || mNewSurfaceNeeded;

        WindowManager.LayoutParams params = null;

       

        

       

        if (mFirst) {

            mFullRedrawNeeded = true;

            mLayoutRequested = true;

            if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL

                    || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {

                // NOTE -- system code, won't try to do compat mode.

                Point size = new Point();

                mDisplay.getRealSize(size);

                desiredWindowWidth = size.x;

                desiredWindowHeight = size.y;

            } else {

                DisplayMetrics packageMetrics =

                    mView.getContext().getResources().getDisplayMetrics();

                desiredWindowWidth = packageMetrics.widthPixels;

                desiredWindowHeight = packageMetrics.heightPixels;

            }

        } else {

            desiredWindowWidth = frame.width();

            desiredWindowHeight = frame.height();

            if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {

                if (DEBUG_ORIENTATION) Log.v(TAG,

                        "View " + host + " resized to: " + frame);

                mFullRedrawNeeded = true;

                mLayoutRequested = true;

                windowSizeMayChange = true;

            }

        }

        boolean insetsChanged = false;

        boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);

        if (layoutRequested) {

            final Resources res = mView.getContext().getResources();

            if (mFirst) {

                // make sure touch mode code executes by setting cached value

                // to opposite of the added touch mode.

                mAttachInfo.mInTouchMode = !mAddedTouchMode;

                ensureTouchModeLocally(mAddedTouchMode);

            } else {

                if (!mPendingOverscanInsets.equals(mAttachInfo.mOverscanInsets)) {

                    insetsChanged = true;

                }

                if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {

                    insetsChanged = true;

                }

                if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {

                    insetsChanged = true;

                }

                if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {

                    mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);

                    if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "

                            + mAttachInfo.mVisibleInsets);

                }

                if (!mPendingOutsets.equals(mAttachInfo.mOutsets)) {

                    insetsChanged = true;

                }

               

        }

            

        // Determine whether to compute insets.

        // If there are no inset listeners remaining then we may still need to compute

        // insets in case the old insets were non-empty and must be reset.

        final boolean computesInternalInsets =

                mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners()

                || mAttachInfo.mHasNonEmptyGivenInternalInsets;

        boolean insetsPending = false;

        int relayoutResult = 0;

            if (mSurfaceHolder != null) {

                // The app owns the surface; tell it about what is going on.

                if (mSurface.isValid()) {

                    // XXX .copyFrom() doesn't work!

                    //mSurfaceHolder.mSurface.copyFrom(mSurface);

                    mSurfaceHolder.mSurface = mSurface;

                }

                mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight);

                mSurfaceHolder.mSurfaceLock.unlock();

                if (mSurface.isValid()) {

                    if (!hadSurface) {

                        mSurfaceHolder.ungetCallbacks();

                        mIsCreating = true;

                        mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder);

                        SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();

                        if (callbacks != null) {

                            for (SurfaceHolder.Callback c : callbacks) {

                                c.surfaceCreated(mSurfaceHolder);

                            }

                        }

                        surfaceChanged = true;

                    }

                    if (surfaceChanged) {

                        mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder,

                                lp.format, mWidth, mHeight);

                        SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();

                        if (callbacks != null) {

                            for (SurfaceHolder.Callback c : callbacks) {

                                c.surfaceChanged(mSurfaceHolder, lp.format,

                                        mWidth, mHeight);

                            }

                        }

                    }

                    mIsCreating = false;

                } else if (hadSurface) {

                    mSurfaceHolder.ungetCallbacks();

                    SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();

                    mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder);

                    if (callbacks != null) {

                        for (SurfaceHolder.Callback c : callbacks) {

                            c.surfaceDestroyed(mSurfaceHolder);

                        }

                    }

                    mSurfaceHolder.mSurfaceLock.lock();

                    try {

                        mSurfaceHolder.mSurface = new Surface();

                    } finally {

                        mSurfaceHolder.mSurfaceLock.unlock();

                    }

                }

            }

                       // Ask host how big it wants to be

//会回调onMeasure方法

                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

                    // Implementation of weights from WindowManager.LayoutParams

                    // We just grow the dimensions as needed and re-measure if

                    // needs be

                    int width = host.getMeasuredWidth();

                    int height = host.getMeasuredHeight();

                    boolean measureAgain = false;

                    if (lp.horizontalWeight > 0.0f) {

                        width += (int) ((mWidth - width) * lp.horizontalWeight);

                        childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,

                                MeasureSpec.EXACTLY);

                        measureAgain = true;

                    }

                    if (lp.verticalWeight > 0.0f) {

                        height += (int) ((mHeight - height) * lp.verticalWeight);

                        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,

                                MeasureSpec.EXACTLY);

                        measureAgain = true;

                    }

                    if (measureAgain) {

                        if (DEBUG_LAYOUT) Log.v(TAG,

                                "And hey let's measure once more: width=" + width

                                + " height=" + height);

                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

                    }

                    layoutRequested = true;

                }

            }

        } else {

            final boolean windowMoved = (mAttachInfo.mWindowLeft != frame.left

                    || mAttachInfo.mWindowTop != frame.top);

            if (windowMoved) {

                if (mTranslator != null) {

                    mTranslator.translateRectInScreenToAppWinFrame(frame);

                }

                mAttachInfo.mWindowLeft = frame.left;

                mAttachInfo.mWindowTop = frame.top;

                // Update the light position for the new window offsets.

                if (mAttachInfo.mHardwareRenderer != null) {

                    mAttachInfo.mHardwareRenderer.setLightCenter(mAttachInfo);

                }

            }

        }

        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);

        boolean triggerGlobalLayoutListener = didLayout

                || mAttachInfo.mRecomputeGlobalAttributes;

        if (didLayout) {

//会回调onLayout方法

            performLayout(lp, desiredWindowWidth, desiredWindowHeight);

            // By this point all views have been sized and positioned

            // We can compute the transparent area

            if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {

                // start out transparent

                // TODO: AVOID THAT CALL BY CACHING THE RESULT?

                host.getLocationInWindow(mTmpLocation);

                mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],

                        mTmpLocation[0] + host.mRight - host.mLeft,

                        mTmpLocation[1] + host.mBottom - host.mTop);

                host.gatherTransparentRegion(mTransparentRegion);

                if (mTranslator != null) {

                    mTranslator.translateRegionInWindowToScreen(mTransparentRegion);

                }

                if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {

                    mPreviousTransparentRegion.set(mTransparentRegion);

                    mFullRedrawNeeded = true;

                    // reconfigure window manager

                    try {

                        mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);

                    } catch (RemoteException e) {

                    }

                }

            }

        }

     

        boolean skipDraw = false;

        mFirst = false;

        mWillDrawSoon = false;

        mNewSurfaceNeeded = false;

        mViewVisibility = viewVisibility;

       

        // Remember if we must report the next draw.

        if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {

            mReportNextDraw = true;

        }

        boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() ||

                viewVisibility != View.VISIBLE;

        if (!cancelDraw && !newSurface) {

            if (!skipDraw || mReportNextDraw) {

                if (mPendingTransitions != null && mPendingTransitions.size() > 0) {

                    for (int i = 0; i < mPendingTransitions.size(); ++i) {

                        mPendingTransitions.get(i).startChangingAnimations();

                    }

                    mPendingTransitions.clear();

                }

//会回调onDraw()方法

                performDraw();

            }

        } else {

            if (viewVisibility == View.VISIBLE) {

                // Try again

                scheduleTraversals();

            } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {

                for (int i = 0; i < mPendingTransitions.size(); ++i) {

                    mPendingTransitions.get(i).endChangingAnimations();

                }

                mPendingTransitions.clear();

            }

        }

        mIsInTraversal = false;

    }

至此流程梳理完毕

总结一下,调用View invalidate方法->View invalidateInternal 方法->ViewGroup invalidateChild方法->ViewRootImplinvalidateChild---->performTraversals->View onDraw,大概主要调用流程如此,

猜你喜欢

转载自blog.csdn.net/Mr_lu_/article/details/80210779