首先要明白invalidate()方法是做什么的?
View#invalidate():

    /**     * Invalidate the whole view. If the view is visible,     * {@link #onDraw(android.graphics.Canvas)} will be called at some point in     * the future.     * 

* This must be called from a UI thread. To call from a non-UI thread, call * {@link #postInvalidate()}. */ public void invalidate() { invalidate(true); }

英文注释的大致意思是:如果View是可见的,使整个View视图无效,然后在未来的某个时间点View的onDraw(android.graphics.Canvas)方法将被调用。

注意: 该方法必须在UI线程,也就是主线程中才能调用。如果要在非UI线程中调用,可以调用View#postInvalidate() 方法,这也是invalidate()和postInvalidate()的区别之一;

也就是说,invalidate()方法是用来刷新重绘当前的View的,如果当前View的布局尺寸、位置没有变化,仅仅是绘制内容变化了,那么我们就可以调用invalidate()方法。

跟踪源码进入 View#invalidate(boolean invalidateCache):

   @UnsupportedAppUsage    public void invalidate(boolean invalidateCache) {        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);    }// View#invalidate(boolean invalidateCache)方法内部,又调用了下面的方法    void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,            boolean fullInvalidate) {        // 这里挑重点代码查看,以免迷失在代码的海洋中,与源码流程分析无关的代码,暂时不关注        ......省略代码        // 这里判断该子View是否可见或者是否处于动画中,如果子View不可见或者没有处于动画中,则不让该子View失效,即该子View不会被重绘        if (skipInvalidate()) {            return;        }        ......省略代码        // 根据View的标记位来判断该子View是否需要重绘,假如View没有任何变化,那么就不需要重绘        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;            }// 设置PFLAG_DIRTY标记位            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);                // 子View的ViewParent就是它的父View即ViewGroup                // 调用了父类ViewParent的invalidateChild()方法                p.invalidateChild(this, damage);            }            // Damage the entire projection receiver, if necessary.            ......省略代码        }    }

该方法中,首先判断该子View是否可见或者是否处于动画中,如果子View不可见或者没有处于动画中,则不让该子View失效,即该子View不会被重绘。然后根据View的标记位来判断该子View是否需要重绘,假如View没有任何变化,那么就不需要重绘。最后调用了父类ViewParent的invalidateChild()方法,子View的ViewParent就是它的父View即ViewGroup。

跟进ViewGroup#invalidateChild(View child, final Rect dirty) 方法:

public final void invalidateChild(View child, final Rect dirty) {......省略代码// parent为当前的ViewGroup    ViewParent parent = this;    if (attachInfo != null) {        final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0;        Matrix childMatrix = child.getMatrix();        final boolean isOpaque = child.isOpaque() && !drawAnimation &&                child.getAnimation() == null && childMatrix.isIdentity();        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;        location[CHILD_LEFT_INDEX] = child.mLeft;        location[CHILD_TOP_INDEX] = child.mTop;        ......省略代码        do {            View view = null;            if (parent instanceof View) {                view = (View) parent;            }            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            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;                }            }            // 调用parent的invalidateChildInParent,即调用ViewGroup的invalidateChildInParent            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);    }}

ViewGroup#invalidateChild(View child, final Rect dirty) 方法内部,不断的do while循环,直到循环到最外层view的invalidateChildInParent方法。
内层的parent是调用的ViewGroup的invalidateChildInParent方法。
最外层的View,即DecorView,也就是调用DecorView的ViewParent#invalidateChildInParent方法;
View的invalidate()方法的源码分析_第1张图片
那么,DecorView的ViewParent是什么呢?上面的图,给出了答案,是ViewRootImpl。下面我们从源码中探寻究竟为何是ViewRootImpl:

在Activity中,当调用 setContentView() 方法后,经过installDecor() -> generateLayout(mDecor) -> mLayoutInflater.inflate(layoutResID, mContentParent)等方法调用后,Activity的布局文件就已成功添加到了DecorView的mContentParent中,此时,DecorView还未添加到Window中。

Activity调用onResume方法,然后调用makeVisible方法后,DecorView才被添加到Window中。

这里简要分析一下Activity的启动过程,不做过多分析,简单写一下,顺序调用ActivityThread的handleLauncheActivity,handleResumeActivity方法,handleResumeActivity方法中首先调用performResumeActivity方法,performResumeActivity方法中调用Activity中的performResume方法,之后调用Activity的onResume方法,最后调用Activity的makeVisible方法,makeVisible方法中会把当前的顶层DecoView通过WindowManager的addView方法添加到WindowManager中,而WindowManager的实现类WindowManagerImpl中调用的是WindowManagerGlobal的addView方法。

如下: 看注释的关系代码,这里实例化了一个ViewRootImpl,调用了setView方法,传入的view参数就是DecorView;

WindowManagerGlobal#addView:

    public void addView(View view, ViewGroup.LayoutParams params,            Display display, Window parentWindow) {......省略代码        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;        if (parentWindow != null) {            parentWindow.adjustLayoutParamsForSubWindow(wparams);        } else {            // If there's no parent, then hardware acceleration for this view is            // set from the application's hardware acceleration setting.            final Context context = view.getContext();            if (context != null                    && (context.getApplicationInfo().flags                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;            }        }        ViewRootImpl root;        View panelParentView = null;        synchronized (mLock) {            // Start watching for system property changes.            ......省略代码            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 {        // view即为addView传入的DecorView            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(View view, WindowManager.LayoutParams attrs, View panelParentView):

   public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {        synchronized (this) {            if (mView == null) {                mView = view;......省略代码                // 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();// 设置DecorView的viewParent为ViewRootImpl                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);                }                // Set up the input pipeline.                ......省略代码        }    }

ViewRootImpl#setView()方法,其中的参数view即为DecorView,在该方法里面调用了view.assigenParent(this),把ViewRootImpl设置为DecorView的ViewParent。

插入了一段小插曲,下面回头跟踪查看
ViewGroup#invalidateChildInParent(final int[] location, final Rect dirty):

public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {        if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {            // either DRAWN, or DRAWING_CACHE_VALID            if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE))                    != FLAG_OPTIMIZE_INVALIDATE) {                // 调用offset方法,把当前dirty区域的坐标转化为父容器中的坐标                dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,                        location[CHILD_TOP_INDEX] - mScrollY);                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {                // 调用union方法,把子dirty区域与父容器的区域求并集,换句话说,dirty区域变成父容器区域                    dirty.union(0, 0, mRight - mLeft, mBottom - mTop);                }                final int left = mLeft;                final int top = mTop;                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {                    if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {                        dirty.setEmpty();                    }                }                location[CHILD_LEFT_INDEX] = left;                location[CHILD_TOP_INDEX] = top;            } else {                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);                }                location[CHILD_LEFT_INDEX] = mLeft;                location[CHILD_TOP_INDEX] = mTop;                mPrivateFlags &= ~PFLAG_DRAWN;            }            mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;            if (mLayerType != LAYER_TYPE_NONE) {                mPrivateFlags |= PFLAG_INVALIDATED;            }// 返回当前View的父容器,以便进行下一次循环            return mParent;        }        return null;    }

调用offset方法,把当前dirty区域的坐标转化为父容器中的坐标。接着调用union方法,把子dirty区域与父容器的区域求并集,换句话说,dirty区域变成父容器区域。最后返回当前视图的父容器,以便进行下一次循环。

由于ViewGroup#invalidateChild() 方法里面的do while循环完最终会调用最外层 ViewRootImpl 里面的 invaludateChild 方法。

ViewRootImpl#invalidateChild(View child, Rect dirty):

    @Override    public void invalidateChild(View child, Rect dirty) {    // 内部调用了invalidateChildInParent方法        invalidateChildInParent(null, dirty);    }

ViewRootImpl#invalidateChildInParent(int[] location, Rect dirty):

 @Override    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {        checkThread();        if (DEBUG_DRAW) Log.v(mTag, "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 方法        invalidateRectOnScreen(dirty);        return null;    }

该方法主要还是dirty区域的计算。然后调用
ViewRootImpl#invalidateRectOnScreen(Rect dirty):

    private void invalidateRectOnScreen(Rect dirty) {        final Rect localDirty = mDirty;        // 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();        }    }

该方法主要是刷新屏幕上面的一个Rect区域,而Rect区域就是调用invalidate方法的那个View大小。然后调用了ViewRootImpl的scheduleTraversals() 方法。

ViewRootImpl#scheduleTraversals():

   @UnsupportedAppUsage    void scheduleTraversals() {    // 注意这个标志位,多次调用 requestLayout,只有当这个标志位为 false 时才有效        if (!mTraversalScheduled) {            mTraversalScheduled = true;            // 通过postSyncBarrier()设置Handler消息的同步屏障            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();            // Choreographer 通过 postCallback 提交一个任务,mTraversalRunnable是要执行的回调            // 有了同步屏障mTraversalRunnable就会被优先执行            mChoreographer.postCallback(                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);            if (!mUnbufferedInputDispatch) {                scheduleConsumeBatchedInput();            }            notifyRendererOfFramePending();            pokeDrawLockIfNeeded();        }    }

Choreographer 通过 postCallback 提交一个任务,mTraversalRunnable是要执行的回调,有了同步屏障mTraversalRunnable就会被优先执行,至于为何有了同步屏障mTraversalRunnable就会被优先执行?可以查看分析Handler之同步屏障机制与Android的屏幕刷新机制在源码中的应用

ViewRootImpl#TraversalRunnable:

    final class TraversalRunnable implements Runnable {        @Override        public void run() {            doTraversal();        }    }

TraversalRunnable 任务方法中又调用了doTraversal() 方法。
ViewRootImpl#doTraversal() :

    void doTraversal() {        if (mTraversalScheduled) {            mTraversalScheduled = false;            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);            if (mProfile) {                Debug.startMethodTracing("ViewAncestor");            }// 走到这里,这里也是很多博客开始分析View的绘制流程时,选择的切入入口            performTraversals();            if (mProfile) {                Debug.stopMethodTracing();                mProfile = false;            }        }    }

performTraversals() 这个方法非常重要,方法非常多,简单讲我们需要关注以下几个方法:
ViewRootImpl#performTraversals() :

private void performTraversals() {        // cache mView since it is used so much below...        final View host = mView;        mIsInTraversal = true;        ......省略代码                if (mFirst || windowShouldResize || insetsChanged ||                viewVisibilityChanged || params != null || mForceNextWindowRelayout) {            mForceNextWindowRelayout = false;                        if (!mStopped || mReportNextDraw) {                boolean focusChangedDueToTouchMode = ensureTouchModeLocally(                        (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);                if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()                        || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||                        updatedConfiguration) {                    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);                    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);                    // Ask host how big it wants to be                    // 关注方法 1 performMeasure 测量方法                    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 (measureAgain) {                    // 关注方法 1 performMeasure 测量方法                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);                    }                    layoutRequested = true;                }            }        } else {            maybeHandleWindowMove(frame);        }        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);        boolean triggerGlobalLayoutListener = didLayout                || mAttachInfo.mRecomputeGlobalAttributes;        if (didLayout) {        // 关注方法 2 performLayout 位置摆放方法            performLayout(lp, mWidth, mHeight);            ......省略代码        }        ......省略代码        boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;        if (!cancelDraw) {            if (mPendingTransitions != null && mPendingTransitions.size() > 0) {                for (int i = 0; i < mPendingTransitions.size(); ++i) {                    mPendingTransitions.get(i).startChangingAnimations();                }                mPendingTransitions.clear();            }            // 关注方法 3 performDraw 绘制方法            performDraw();        } else {            ......省略代码        }        mIsInTraversal = false;    }

ViewRootImpl#performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) :

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {        if (mView == null) {            return;        }        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");        try {        // 调用View的measure方法,measure方法内部又会调用View的onMeasure方法对View进行测量            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);        } finally {            Trace.traceEnd(Trace.TRACE_TAG_VIEW);        }    }

该方法内部调用了View的measure方法,measure方法内部又会调用View的onMeasure方法对View进行测量。

ViewRootImpl#performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight):

    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,            int desiredWindowHeight) {        mLayoutRequested = false;        mScrollMayChange = true;        mInLayout = true;        final View host = mView;        if (host == null) {            return;        }          try {            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());            mInLayout = false;            int numViewsRequestingLayout = mLayoutRequesters.size();            if (numViewsRequestingLayout > 0) {                ......省略代码                if (validLayoutRequesters != null) {             ......省略代码                    measureHierarchy(host, lp, mView.getContext().getResources(),                            desiredWindowWidth, desiredWindowHeight);                    mInLayout = true;                    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());                    ......省略代码                }            }        } finally {            Trace.traceEnd(Trace.TRACE_TAG_VIEW);        }        mInLayout = false;    }

该方法内部调用了View的layout方法,layout方法内部又会调用View的onLayout方法对View进行布局摆放。

ViewRootImpl#performDraw() :

private void performDraw() {......省略代码        try {            boolean canUseAsync = draw(fullRedrawNeeded);            if (usingAsyncReport && !canUseAsync) {                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);                usingAsyncReport = false;            }        } finally {            mIsDrawing = false;            Trace.traceEnd(Trace.TRACE_TAG_VIEW);        }    }

该方法中调用了ViewRootImpl#draw()方法,ViewRootImpl#draw()方法又调用了ViewRootImpl#drawSoftware()方法,ViewRootImpl#drawSoftware()方法中,当前的View调用了View#draw() 方法,即mView.draw(canvas)对当前的View进行绘制;

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

至此,View#invalidate() 方法的源码流程分析完毕;

如果对 View#requestLayout() 方法感兴趣的,可以查看下一篇View的requestLayout()方法的源码分析

更多相关文章

  1. Android Robotium的自动化代码
  2. Android学习札记36:一个关于onSaveInstanceState ()方法的例子
  3. Android代码实现飞行模式的打开
  4. Android对应用程序的资源文件xml解析的源代码在哪里
  5. Android调用系统的发邮件方法
  6. Android轮播图Banner使用方法
  7. android:软件的安装和卸载源代码

随机推荐

  1. Android负责人:完全开放和一致体验是挑战
  2. Android(安卓)三类框架的理解以及MVVM框
  3. android那些事——android的成长
  4. Android系统架构-----Android的系统体系
  5. 【android基础】之在程序中设置android:g
  6. Android软件工程师之不归路
  7. android最佳实践(二)
  8. Android(安卓)4高级编程(第3版) 试读
  9. cocos2dx打包Android出现“未指定Android
  10. Android(安卓)中文API合集(3)(83篇)(chm