源码剖析:知道原理为先,别一初步就往深处走, 要墨守成规 #invalidate()源码

    public void invalidate() {
        invalidate(true);
    }
    /**
     * This is where the invalidate() work actually happens. A full invalidate()
     * causes the drawing cache to be invalidated, but this function can be
     * called with invalidateCache set to false to skip that invalidation step
     * for cases that do not need it (for example, a component that remains at
     * the same dimensions with the same content).
     *
     * @param invalidateCache Whether the drawing cache for this view should be
     *            invalidated as well. This is usually true for a full
     *            invalidate, but may be set to false if the View's contents or
     *            dimensions have not changed.
     * @hide
     */
    public void invalidate(boolean invalidateCache) {
        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;
        }
        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();
                }
            }
        }
    }

#invalidateInternal() – **首要调用了`invalidateInternal()`;** “` void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) “`
  • invalidateInternal()中的skipInvalidate()必定看得懂, 即契合某几个条件 【 如界面不行见了((...) != VISIBLE)、 或许没有设置动画mCurrentAnimation == null等 (mCurrentAnimation 在View源码是一个全局变量,跟动画有关,可以看一下源码) 】, 则跳过invalidate()的调用 【直接return了,更不用说会调用onDraw()了】:
//invalidateInternal() 中的 skipInvalidate()
        if (skipInvalidate()) {
            return;
        }
...
//skipInvalidate() 源码
    /**
     * Do not invalidate views which are not visible and which are not running an animation. They
     * will not get drawn and they should not set dirty flags as if they will be drawn
     */
    private boolean skipInvalidate() {
        return (mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null &&
                (!(mParent instanceof ViewGroup) ||
                        !((ViewGroup) mParent).isViewTransitioning(this));
    }
  • 往下继续看invalidateInternal()的代码,可以翻到: 这儿调用了ViewParentinvalidateChild()
    自定义View---invalidate()源码剖析
    ViewParent自然是往ViewGroup的源码看,
    自定义View---invalidate()源码剖析
    这儿有个parent实例的赋值,直接赋值this, 接着主要看这儿有个do...while()循环:
    自定义View---invalidate()源码剖析
    自定义View---invalidate()源码剖析
    !!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!! 这个do...while()循环的意图就是不断地拿这个parent实例, 去进行一系列的操作; 其间每一次循环, 又拿这个parent实例去调用invalidateChildInParent(), 去获取这个parent实例的parent实例: !!!!!!!!!!!!!!!! 如此不断循环, 毕竟会调用到最外层ViewGroup【View】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);
        }

invalidateChildInParent()源码:【会回来一个ViewParent实例或许空;】

自定义View---invalidate()源码剖析

 @Deprecated
    @Override
    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))
      ...
                }
                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 {
...
            return mParent;
        }
        return null;
    }

接着这部分要在本地的SDK目录中找到这个ViewRootImpl源码文件, 然后把它拖到AS中阅读:

自定义View---invalidate()源码剖析
接着是:
自定义View---invalidate()源码剖析
其间毕竟调用了scheduleTraversals();
自定义View---invalidate()源码剖析
接着往下点,
自定义View---invalidate()源码剖析
接着往下点,
自定义View---invalidate()源码剖析
!!!!!!!!!!!!!!!!!!!!! 好了,要点就是这个performTraversals()方法,它在制造流程中非常重要。 【一个近千行的方法】
自定义View---invalidate()源码剖析
!!!!!!!!!!!!!!!!!!!!! 其间又调用了三个重要的方法: performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
自定义View---invalidate()源码剖析
performLayout(lp, mWidth, mHeight);
自定义View---invalidate()源码剖析
自定义View---invalidate()源码剖析
performDraw()
自定义View---invalidate()源码剖析
!!!!!!!!!!!!!!!!! setContentView()的流程中,调用了以上三个方法, invalidate()主要是调用了performDraw()方法, 因为源码中存在条件判别,所以是不会进performMeasure()performLayout()两个方法的; !!!!!!!!!!!!!!!!!

所以下面主要看performDraw()方法, performDraw()中有一个draw方法, private boolean draw(boolean fullRedrawNeeded)

自定义View---invalidate()源码剖析
其间又调用了drawSoftware()
自定义View---invalidate()源码剖析
drawSoftware()中,呈现了CanvasmSurfacemSurface锁定了Canvas【lockCanvas()】;大约了解一下就好;
自定义View---invalidate()源码剖析
接着呢,又调用了View的draw()方法:
自定义View---invalidate()源码剖析
View的draw()的源码 我们在《自定义View | 基础概述 & 自定义TextView实战 & 根据源码剖析自定义View承继自ViewGroup时无法正常制造的问题》这篇笔记中有提及到, 下面是源码注释中总结的六个过程:
自定义View---invalidate()源码剖析

小拓展——子线程中为什么不能更新UI? 源码视点稍微剖析一下: 调用setText()、setImageView()等方法, 毕竟都会调到ViewRootImplcheckThread()checkThread()是用来检测线程的;

自定义View---invalidate()源码剖析
Thread.currentThread()是子线程, mThread是在结构函数中初始化的,
自定义View---invalidate()源码剖析
这儿的是主线程;


#归结一下invalidate()的流程 **扼要阅读完源码了,归结一下`invalidate()`的大约流程: 首要从调用了`invalidate()`的`View`初步, 一路往上,跑到最外层的ViewRoot:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1091e9a6e3754f788555f4880001c03e~tplv-k3u1fbpfcp-zoom-1.image)用最外层的View调用`draw()`, `draw()`如源码第四步有一个`dispatchDraw()`,又会一路往下画, 不断的制造`子孩子`,再制造`子孩子`的`子孩子`, 毕竟制造到 调用了`invalidate()`的`View`的 `onDraw()`方法;
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 所以当我们简简单单地调用一个View的`invalidate()`时, 它牵连的是`整个布局中`的`View实例`, 而不仅仅是 其时调用`invalidate()`的`View实例`一个 罢了; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!**