美文网首页Android
Android-invalidate原理分析

Android-invalidate原理分析

作者: zzq_nene | 来源:发表于2020-06-11 14:30 被阅读0次

    一、postInvalidate和invalidate

    其实postInvalidate最终也是调用的invalidate来实现。

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

    AttachInfo.mViewRootImpl其实就是ViewRootImpl对象

        public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
            Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
            mHandler.sendMessageDelayed(msg, delayMilliseconds);
        }
    

    这里的mHandler对象,其实是ViewRootImpl内部类ViewRootHandler对象,

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_INVALIDATE:
                        ((View) msg.obj).invalidate();
                        break;
            ....
            }
    

    而这里的msg.obj其实就是View对象

    二、invalidate源码分析

    image.png

    从网上拿来用的一张图,自己就不画了。

    View.invalidate
        public void invalidate() {
            invalidate(true);
        }
    
        @UnsupportedAppUsage
        public void invalidate(boolean invalidateCache) {
            invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
        }
    
    View.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;
            }
    
            // Reset content capture caches
            mCachedContentCaptureSession = null;
    
            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);
                    // 调用了ViewParent的invalidateChild方法,而ViewGroup实现了ViewParent接口
                    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();
                    }
                }
            }
        }
    

    根据查找ViewParent接口实现我们可以知道,ViewGroup是ViewParent的实现类,所以这里是调用了ViewGroup.invalidateChild()

    ViewGroup.invalidateChild()
        @Override
        public final void invalidateChild(View child, final Rect dirty) {
            final AttachInfo attachInfo = mAttachInfo;
            if (attachInfo != null && attachInfo.mHardwareAccelerated) {
                // HW accelerated fast path
                onDescendantInvalidated(child, child);
                return;
            }
    
            ViewParent parent = this;
            if (attachInfo != null) {
                
                ...
    
                do {
                    ...
                    parent = parent.invalidateChildInParent(location, dirty);
                    ...
                } while (parent != null);
            }
        }
    

    因为parent是在invalidateChild中赋值的,其实就是ViewGroup本身,所以是调用的ViewGroup的invalidateChildInParent方法

    ViewGroup.invalidateChildInParent()
        @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))
                        != FLAG_OPTIMIZE_INVALIDATE) {
                    dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
                            location[CHILD_TOP_INDEX] - mScrollY);
                    if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
                        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;
                }
    
                return mParent;
            }
    
            return null;
        }
    

    但是因为ViewGroup.invalidateChild方法中执行的是一个do-while循环,不断的循环返回View的mParent,而ViewGroup最终的mParent其实是DecorView,而DecorView.mParent其实就是ViewRootImpl,所以这里最终会调用到ViewRootImpl.invalidateChildInParent

    ViewRootImpl.invalidateChildInParent()
        @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(dirty);
    
            return null;
        }
    
    ViewRootImpl.invalidateRectOnScreen()
        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();
            }
        }
    
    ViewGroup.scheduleTraversals()
        @UnsupportedAppUsage
        void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    

    scheduleTraversals()方法内部通过调用Choreographer.postCallback方法,其内部就是通过Handler,然后在doFrame中调用doCallbacks,通过遍历CallbackRecord这个链表,执行保持在CallbackRecord中的action(即Runnable)的run方法。就是TraversalRunnable对象mTraversalRunnable

        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();
                callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                        now / TimeUtils.NANOS_PER_MS);
                if (callbacks == null) {
                    return;
                }
                ...
            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);
            }
        }
    

    CallbackRecord是一个链表结构,在这里最终就会判断是否是帧callback token,如果不是,则调用action.run(),这里的action其实就是TraversalRunnable

        private static final class CallbackRecord {
            public CallbackRecord next;
            public long dueTime;
            public Object action; // Runnable or FrameCallback
            public Object token;
    
            @UnsupportedAppUsage
            public void run(long frameTimeNanos) {
                if (token == FRAME_CALLBACK_TOKEN) {
                    ((FrameCallback)action).doFrame(frameTimeNanos);
                } else {
                    ((Runnable)action).run();
                }
            }
        }
    

    执行到TraversalRunnable的run()

    ViewRootImpl.TraversalRunnable内部类
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    

    调用了ViewRootImpl.doTraversal()

    ViewRootImpl.doTraversal()
        void doTraversal() {
            if (mTraversalScheduled) {
                mTraversalScheduled = false;
                mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    
                if (mProfile) {
                    Debug.startMethodTracing("ViewAncestor");
                }
    
                performTraversals();
    
                if (mProfile) {
                    Debug.stopMethodTracing();
                    mProfile = false;
                }
            }
        }
    

    在这里会调用performTraversals()

    ViewRootImpl.performTraversals()

    performTraversals方法比较长,所以看一部分

        private void performTraversals() {
            ...
            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();
                }
    
                performDraw();
            } else {
                if (isViewVisible) {
                    // Try again
                    scheduleTraversals();
                } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                    for (int i = 0; i < mPendingTransitions.size(); ++i) {
                        mPendingTransitions.get(i).endChangingAnimations();
                    }
                    mPendingTransitions.clear();
                }
            }
        }
    

    这个方法主要是做了三个操作,即调用performMeasure、performLayout、performDraw,而invalidate流程是否会触发performMeasure和performLayout姑且不论,因为这里需要经过繁琐的标志位的判断,但是会执行performDraw。而performMeasure和performLayout分别是measure过程和layout过程,是由View.requestLayout()或者WindowManagerImpl.addView调用WindowManagerGlobal.addView这个两种情况最终调用了ViewRootImpl.requestLayout来触发调用performMeasure和performLayout。

        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()
        private boolean draw(boolean fullRedrawNeeded) {
            Surface surface = mSurface;
            if (!surface.isValid()) {
                return false;
            }
    
            if (DEBUG_FPS) {
                trackFPS();
            }
    
            if (!sFirstDrawComplete) {
                synchronized (sFirstDrawHandlers) {
                    sFirstDrawComplete = true;
                    final int count = sFirstDrawHandlers.size();
                    for (int i = 0; i< count; i++) {
                        mHandler.post(sFirstDrawHandlers.get(i));
                    }
                }
            }
    
            scrollToRectOrFocus(null, false);
    
            if (mAttachInfo.mViewScrollChanged) {
                mAttachInfo.mViewScrollChanged = false;
                mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
            }
    
            boolean animating = mScroller != null && mScroller.computeScrollOffset();
            final int curScrollY;
            if (animating) {
                curScrollY = mScroller.getCurrY();
            } else {
                curScrollY = mScrollY;
            }
            if (mCurScrollY != curScrollY) {
                mCurScrollY = curScrollY;
                fullRedrawNeeded = true;
                if (mView instanceof RootViewSurfaceTaker) {
                    ((RootViewSurfaceTaker) mView).onRootViewScrollYChanged(mCurScrollY);
                }
            }
    
            final float appScale = mAttachInfo.mApplicationScale;
            final boolean scalingRequired = mAttachInfo.mScalingRequired;
    
            final Rect dirty = mDirty;
            if (mSurfaceHolder != null) {
                // The app owns the surface, we won't draw.
                dirty.setEmpty();
                if (animating && mScroller != null) {
                    mScroller.abortAnimation();
                }
                return false;
            }
    
            if (fullRedrawNeeded) {
                dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
            }
    
            if (DEBUG_ORIENTATION || DEBUG_DRAW) {
                Log.v(mTag, "Draw " + mView + "/"
                        + mWindowAttributes.getTitle()
                        + ": dirty={" + dirty.left + "," + dirty.top
                        + "," + dirty.right + "," + dirty.bottom + "} surface="
                        + surface + " surface.isValid()=" + surface.isValid() + ", appScale:" +
                        appScale + ", width=" + mWidth + ", height=" + mHeight);
            }
    
            mAttachInfo.mTreeObserver.dispatchOnDraw();
    
            int xOffset = -mCanvasOffsetX;
            int yOffset = -mCanvasOffsetY + curScrollY;
            final WindowManager.LayoutParams params = mWindowAttributes;
            final Rect surfaceInsets = params != null ? params.surfaceInsets : null;
            if (surfaceInsets != null) {
                xOffset -= surfaceInsets.left;
                yOffset -= surfaceInsets.top;
    
                // Offset dirty rect for surface insets.
                dirty.offset(surfaceInsets.left, surfaceInsets.right);
            }
    
            boolean accessibilityFocusDirty = false;
            final Drawable drawable = mAttachInfo.mAccessibilityFocusDrawable;
            if (drawable != null) {
                final Rect bounds = mAttachInfo.mTmpInvalRect;
                final boolean hasFocus = getAccessibilityFocusedRect(bounds);
                if (!hasFocus) {
                    bounds.setEmpty();
                }
                if (!bounds.equals(drawable.getBounds())) {
                    accessibilityFocusDirty = true;
                }
            }
    
            mAttachInfo.mDrawingTime =
                    mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
    
            boolean useAsyncReport = false;
            if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
                if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
                    ...
                } else {
                    ...
    
                    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
                            scalingRequired, dirty, surfaceInsets)) {
                        return false;
                    }
                }
            }
    
            if (animating) {
                mFullRedrawNeeded = true;
                scheduleTraversals();
            }
            return useAsyncReport;
        }
    
    ViewRootImpl.drawSoftWare()
        private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
                boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
    
            ...
    
            try {
                ...
    
                mView.draw(canvas);
    
                drawAccessibilityFocusedDrawableIfNeeded(canvas);
            } finally {
                ...
            }
            return true;
        }
    

    从这里可以看出,最终是调用了View.draw()方法。
    其实invalidate()方法的使用,就是重新触发一次draw流程,进行绘制。通过View.invalidate()方法在ViewGroup的invalidateChild()方法中执行一个do-while循环,最终调用了ViewRootImpl.invalidateChildInParent(),而在ViewRootImpl.invalidateChildInParent()方法中最终触发调用了ViewRootImpl.scheduleTraversals(),又一次执行了与measure过程和layout过程一致的流程,因为scheduleTraversals()方法会通过一系列的调用最终调用TraversalRunnable.run()方法,在TraversalRunnable.run()方法中会调用performTraversals(),这个方法其实就是做了三件事:measure、layout和draw

    参考下
    https://www.jianshu.com/p/33b855dcde5a

    相关文章

      网友评论

        本文标题:Android-invalidate原理分析

        本文链接:https://www.haomeiwen.com/subject/rubgzhtx.html