invalidate和postInvalidate使用場(chǎng)景及源碼解析

invalidate.png

invalidate

在自定view時(shí),經(jīng)常要調(diào)用invalidate方法去刷新ui,調(diào)用簡(jiǎn)單的invalidate方法就可以將ui重新刷新拨与,其實(shí)在源碼中相當(dāng)多的處理哩治,接下來(lái)就慢慢去看看invalidate方法的源碼秃踩;在調(diào)用invalidate方法后就會(huì)去調(diào)用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.
     * <p>
     * This must be called from a UI thread. To call from a non-UI thread, call
     * {@link #postInvalidate()}.
     */
    public void invalidate() {
        invalidate(true);
    }

通過(guò)invalidate方法的源碼注釋可以大致知道三點(diǎn):
1.調(diào)用invalidate方法會(huì)刷新ui锚扎,前提是該view是可見的吞瞪;
2.調(diào)用invalidate方法后,onDraw方法也會(huì)被調(diào)用驾孔;
3.invalidate方法的調(diào)用必須在ui線程中芍秆,如果在非ui線程中可以使用postInvalidate方法更新ui,這也是invalidate和postInvalidate方法的區(qū)別

調(diào)用View中的invalidate方法后翠勉,接著會(huì)調(diào)用View中的invalidateInternal方法妖啥,在invalidateInternal方法中會(huì)調(diào)用skinInvalidate方法判斷該view是否可見,該view是否有動(dòng)畫对碌,接著會(huì)調(diào)用ViewParent中的invalidateChild方法荆虱;

void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
            boolean fullInvalidate) {
        if (mGhostView != null) {
            mGhostView.invalidate(true);
            return;
        }
//調(diào)用skinInvalidate方法判斷該view是否可見,是否有動(dòng)畫朽们,
//如果該view是不可見的怀读,并且是沒有動(dòng)畫的就是返回,不進(jìn)行更新
        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;
//將mParent賦值給ViewParent對(duì)象
            final ViewParent p = mParent;
            if (p != null && ai != null && l < r && t < b) {
                final Rect damage = ai.mTmpInvalRect;
                damage.set(l, t, r, b);
//調(diào)用ViewParent中的invalidateChild方法
                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();
            }
        }
    }

現(xiàn)在就到了ViewParent中的invalidateChild方法骑脱,會(huì)發(fā)現(xiàn)ViewParent是一個(gè)接口磕仅,就需要去找它的實(shí)現(xiàn)類诀拭,ViewGroup和ViewRootImpl都是它的實(shí)現(xiàn)類左刽,所以就找ViewGroup中的invalidateChild方法冈止;

    @Override
    public final void invalidateChild(View child, final Rect dirty) {
//將當(dāng)前ViewGroup賦值給ViewParent,
//這樣ViewParent就不會(huì)為null拥娄,在下面的whlie循環(huán)中就可以進(jìn)行循環(huán)
        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
            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.
            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;
            location[CHILD_LEFT_INDEX] = child.mLeft;
            location[CHILD_TOP_INDEX] = child.mTop;
            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));
            }
//進(jìn)行while循環(huán)
            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;
                    }
                }
//這里會(huì)不停的調(diào)用ViewParent中的invalidateChildInParent方法蚊锹,
//并返回一個(gè)ViewParent對(duì)象,會(huì)一直往外遍歷稚瘾,知道最外層ViewRootImpl牡昆,
//返回的ViewParent對(duì)象為null就跳出該循環(huán)
                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);
        }
    }

invalidateChild是不能被重寫的,在該方法中的while循環(huán)中會(huì)不聽的調(diào)用ViewParent中的invalidateChildInParent方法孟抗,并返回一個(gè)ViewParent對(duì)象迁杨,會(huì)一直往外層遍歷钻心,直到ViewRootImpl中的invalidateChildInParent方法,如果返回ViewParent為null就跳出當(dāng)前循環(huán)铅协;
遍歷到最外層后就會(huì)去調(diào)動(dòng)ViewRootImpl中的invalidateChildInParent方法捷沸;

@Override
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
//調(diào)用checkThread去檢測(cè)當(dāng)前線程是不是ui線程,如果不是就會(huì)拋異常
        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);
            }
        }
//調(diào)動(dòng)invalidateRectOnScreen方法去更新繪制ui
        invalidateRectOnScreen(dirty);

        return null;
    }

在ViewRootImpl的invalidateChildInParent方法中首先會(huì)調(diào)用checkThread方法去檢測(cè)當(dāng)前線程是否是ui線程狐史,如果不是ui線程痒给,就會(huì)拋異常,這也是不能在非ui線程更新ui的原因骏全;接著就會(huì)調(diào)用invalidateRectOnScreen方法苍柏;在invalidateRectOnScreen方法中就會(huì)去調(diào)用scheduleTraversals方法;在scheduleTraversals方法中就會(huì)調(diào)用postCallBack并傳入一個(gè)TraversalRunnable對(duì)象姜贡;在TraversalRunnable對(duì)象的run方法中會(huì)去調(diào)用doTraversal方法试吁;

void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
//調(diào)用performTraversals方法進(jìn)行繪制準(zhǔn)備工作;
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

在performTraversals方法中就會(huì)去調(diào)用performMearsure楼咳、performLayout熄捍、performDraw等方法,不過(guò)在invalidate刷新ui的時(shí)候只會(huì)調(diào)用performDraw方法母怜,所有只去看performDraw方法就可以了余耽;在performDraw中就會(huì)去調(diào)用draw方法;在draw中就會(huì)去調(diào)用drawSofeware方法苹熏;

**
     * @return true if drawing was successful, false if an error occurred
     */
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {

        // Draw with software renderer.
//定義一個(gè)Canvas畫布
        final Canvas canvas;
        try {
            final int left = dirty.left;
            final int top = dirty.top;
            final int right = dirty.right;
            final int bottom = dirty.bottom;

            canvas = mSurface.lockCanvas(dirty);

            // The dirty rectangle can be modified by Surface.lockCanvas()
            //noinspection ConstantConditions
            if (left != dirty.left || top != dirty.top || right != dirty.right
                    || bottom != dirty.bottom) {
                attachInfo.mIgnoreDirtyState = true;
            }

            // TODO: Do this in native
            canvas.setDensity(mDensity);
        } catch (Surface.OutOfResourcesException e) {
            handleOutOfResourcesException(e);
            return false;
        } catch (IllegalArgumentException e) {
            Log.e(mTag, "Could not lock surface", e);
            // Don't assume this is due to out of memory, it could be
            // something else, and if it is something else then we could
            // kill stuff (or ourself) for no reason.
            mLayoutRequested = true;    // ask wm for a new surface next time.
            return false;
        }

        try {
            if (DEBUG_ORIENTATION || DEBUG_DRAW) {
                Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
                        + canvas.getWidth() + ", h=" + canvas.getHeight());
                //canvas.drawARGB(255, 255, 0, 0);
            }

            // If this bitmap's format includes an alpha channel, we
            // need to clear it before drawing so that the child will
            // properly re-composite its drawing on a transparent
            // background. This automatically respects the clip/dirty region
            // or
            // If we are applying an offset, we need to clear the area
            // where the offset doesn't appear to avoid having garbage
            // left in the blank areas.
            if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
            }

            dirty.setEmpty();
            mIsAnimating = false;
            mView.mPrivateFlags |= View.PFLAG_DRAWN;

            if (DEBUG_DRAW) {
                Context cxt = mView.getContext();
                Log.i(mTag, "Drawing: package:" + cxt.getPackageName() +
                        ", metrics=" + cxt.getResources().getDisplayMetrics() +
                        ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
            }
            try {
                canvas.translate(-xoff, -yoff);
                if (mTranslator != null) {
                    mTranslator.translateCanvas(canvas);
                }
                canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
                attachInfo.mSetIgnoreDirtyState = false;
//調(diào)用view中的draw方法碟贾,并將定義的canvas畫布傳入
                mView.draw(canvas);

                drawAccessibilityFocusedDrawableIfNeeded(canvas);
            } finally {
                if (!attachInfo.mSetIgnoreDirtyState) {
                    // Only clear the flag if it was not set during the mView.draw() call
                    attachInfo.mIgnoreDirtyState = false;
                }
            }
        } finally {
            try {
                surface.unlockCanvasAndPost(canvas);
            } catch (IllegalArgumentException e) {
                Log.e(mTag, "Could not unlock surface", e);
                mLayoutRequested = true;    // ask wm for a new surface next time.
                //noinspection ReturnInsideFinallyBlock
                return false;
            }

            if (LOCAL_LOGV) {
                Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost");
            }
        }
        return true;
    }

終于到了View中的draw方法了;

@CallSuper
    public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
//定義dirtyOpaque 標(biāo)識(shí)轨域,用來(lái)控制drawBackground onDraw等方法的調(diào)用
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1. Draw the background    繪制背景
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content    繪制內(nèi)容
         *      4. Draw children   繪制子view
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */

        // Step 1, draw the background, if needed
        int saveCount;

        if (!dirtyOpaque) {
//沒有設(shè)置背景袱耽,默認(rèn)的ViewGroup不會(huì)調(diào)用該方法
            drawBackground(canvas);
        }

        // skip step 2 & 5 if possible (common case)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, draw the content  默認(rèn)的ViewGroup不會(huì)調(diào)用該方法
            if (!dirtyOpaque) onDraw(canvas);

            // Step 4, draw the children
            dispatchDraw(canvas);

            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);

            // we're done...
            return;
        }

        /*
         * Here we do the full fledged routine...
         * (this is an uncommon case where speed matters less,
         * this is why we repeat some of the tests that have been
         * done above)
         */

        boolean drawTop = false;
        boolean drawBottom = false;
        boolean drawLeft = false;
        boolean drawRight = false;

        float topFadeStrength = 0.0f;
        float bottomFadeStrength = 0.0f;
        float leftFadeStrength = 0.0f;
        float rightFadeStrength = 0.0f;

        // Step 2, save the canvas' layers
        int paddingLeft = mPaddingLeft;

        final boolean offsetRequired = isPaddingOffsetRequired();
        if (offsetRequired) {
            paddingLeft += getLeftPaddingOffset();
        }

        int left = mScrollX + paddingLeft;
        int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
        int top = mScrollY + getFadeTop(offsetRequired);
        int bottom = top + getFadeHeight(offsetRequired);

        if (offsetRequired) {
            right += getRightPaddingOffset();
            bottom += getBottomPaddingOffset();
        }

        final ScrollabilityCache scrollabilityCache = mScrollCache;
        final float fadeHeight = scrollabilityCache.fadingEdgeLength;
        int length = (int) fadeHeight;

        // clip the fade length if top and bottom fades overlap
        // overlapping fades produce odd-looking artifacts
        if (verticalEdges && (top + length > bottom - length)) {
            length = (bottom - top) / 2;
        }

        // also clip horizontal fades if necessary
        if (horizontalEdges && (left + length > right - length)) {
            length = (right - left) / 2;
        }

        if (verticalEdges) {
            topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
            drawTop = topFadeStrength * fadeHeight > 1.0f;
            bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
            drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
        }

        if (horizontalEdges) {
            leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
            drawLeft = leftFadeStrength * fadeHeight > 1.0f;
            rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
            drawRight = rightFadeStrength * fadeHeight > 1.0f;
        }

        saveCount = canvas.getSaveCount();

        int solidColor = getSolidColor();
        if (solidColor == 0) {
            final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

            if (drawTop) {
                canvas.saveLayer(left, top, right, top + length, null, flags);
            }

            if (drawBottom) {
                canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
            }

            if (drawLeft) {
                canvas.saveLayer(left, top, left + length, bottom, null, flags);
            }

            if (drawRight) {
                canvas.saveLayer(right - length, top, right, bottom, null, flags);
            }
        } else {
            scrollabilityCache.setFadeColor(solidColor);
        }

        // Step 3, draw the content
        if (!dirtyOpaque) onDraw(canvas);

        // Step 4, draw the children
        dispatchDraw(canvas);

        // Step 5, draw the fade effect and restore layers
        final Paint p = scrollabilityCache.paint;
        final Matrix matrix = scrollabilityCache.matrix;
        final Shader fade = scrollabilityCache.shader;

        if (drawTop) {
            matrix.setScale(1, fadeHeight * topFadeStrength);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, right, top + length, p);
        }

        if (drawBottom) {
            matrix.setScale(1, fadeHeight * bottomFadeStrength);
            matrix.postRotate(180);
            matrix.postTranslate(left, bottom);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, bottom - length, right, bottom, p);
        }

        if (drawLeft) {
            matrix.setScale(1, fadeHeight * leftFadeStrength);
            matrix.postRotate(-90);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, left + length, bottom, p);
        }

        if (drawRight) {
            matrix.setScale(1, fadeHeight * rightFadeStrength);
            matrix.postRotate(90);
            matrix.postTranslate(right, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(right - length, top, right, bottom, p);
        }

        canvas.restoreToCount(saveCount);

        // Overlay is part of the content and draws beneath Foreground
        if (mOverlay != null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }

        // Step 6, draw decorations (foreground, scrollbars)
        onDrawForeground(canvas);
    }

最終會(huì)去調(diào)用View中的onDraw方法,然后在自定定義view中重寫onDraw方法干发,進(jìn)行ui刷新和繪制扛邑,在View的draw方法中繪制工作分為以下:
1.調(diào)用drawBackground方法繪制背景
2.調(diào)用onDraw方法繪制內(nèi)容
3.調(diào)用dispatchDraw方法繪制子view
4.調(diào)用onDrawForeground繪制裝飾
這些方法中默認(rèn)的ViewGroup不會(huì)走drawBackground和onDraw方法,在繼承自ViewGroup的自定義view中要繪制的話铐然,可以重寫onDraw方法,并設(shè)置背景恶座,或者重寫dispatchDraw方法搀暑,在里面進(jìn)行繪制,方法有很多跨琳;
上面大致就是對(duì)invalidate的源碼分析自点,接下來(lái)看postInvalidate方法;

postInvalidate

/**
     * <p>Cause an invalidate to happen on a subsequent cycle through the event loop.
     * Use this to invalidate the View from a non-UI thread.</p>
     *
     * <p>This method can be invoked from outside of the UI thread
     * only when this View is attached to a window.</p>
     *
     * @see #invalidate()
     * @see #postInvalidateDelayed(long)
     */
    public void postInvalidate() {
        postInvalidateDelayed(0);
    }

通過(guò)源碼注釋很明顯該方法被調(diào)用于ui線程以外去更新繪制ui脉让;接著會(huì)去調(diào)用postInvalidateDelayed方法桂敛;

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

在postInvalidateDelayed方法中就直接去調(diào)用ViewRootImpl中的dispatchInvalidateDelayed方法功炮;

public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
//實(shí)例化一個(gè)Message消息對(duì)象
        Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
//發(fā)送消息
        mHandler.sendMessageDelayed(msg, delayMilliseconds);
    }

在dispatchInvalidateDelayed方法中會(huì)通過(guò)handler實(shí)例化一個(gè)Message對(duì)象,然后通過(guò)handler發(fā)送消息术唬;這里的hanlder是ViewRootHandler薪伏,在ViewRootHandler的handleMessage方法中就會(huì)根據(jù)消息標(biāo)識(shí)進(jìn)行處理;

final class ViewRootHandler extends Handler {
        @Override
        public String getMessageName(Message message) {
            ......
            return super.getMessageName(message);
        }

        @Override
        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            if (msg.what == MSG_REQUEST_KEYBOARD_SHORTCUTS && msg.obj == null) {
                // Debugging for b/27963013
                throw new NullPointerException(
                        "Attempted to call MSG_REQUEST_KEYBOARD_SHORTCUTS with null receiver:");
            }
            return super.sendMessageAtTime(msg, uptimeMillis);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
//根據(jù)消息標(biāo)識(shí)來(lái)進(jìn)行處理
            case MSG_INVALIDATE:
//調(diào)用view中invalidate方法
                ((View) msg.obj).invalidate();
                break;
            ......
            }
        }
    }

走到這里會(huì)看到postInvalidate最終還是通過(guò)調(diào)用view中的invaidate方法進(jìn)行view繪制更新粗仓,后面流程可以看上面invalidate的繪制流程嫁怀。

結(jié)論

invalidate是在ui線程中使用,postInvalidate是在非ui線程中使用借浊,postInvalidate通過(guò)ViewRootHandler消息切換到主線程中塘淑,最終還是通過(guò)調(diào)用View中的invalidate方法進(jìn)行ui的繪制更新,不管是invalidate還是postInvalidate只有當(dāng)該View為可見時(shí)才會(huì)進(jìn)行view的更新繪制蚂斤,調(diào)用invalidate或者postInvalidate更新繪制view時(shí)存捺,只會(huì)調(diào)用onDraw方法,不會(huì)調(diào)用onMeasure和onLayout方法曙蒸。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末捌治,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子逸爵,更是在濱河造成了極大的恐慌具滴,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件师倔,死亡現(xiàn)場(chǎng)離奇詭異构韵,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)趋艘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門疲恢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人瓷胧,你說(shuō)我怎么就攤上這事显拳。” “怎么了搓萧?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵杂数,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我瘸洛,道長(zhǎng)揍移,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任反肋,我火速辦了婚禮那伐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己罕邀,他們只是感情好畅形,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著诉探,像睡著了一般日熬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上阵具,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天碍遍,我揣著相機(jī)與錄音,去河邊找鬼阳液。 笑死怕敬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的帘皿。 我是一名探鬼主播东跪,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鹰溜!你這毒婦竟也來(lái)了虽填?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤曹动,失蹤者是張志新(化名)和其女友劉穎斋日,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體墓陈,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恶守,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贡必。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兔港。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖仔拟,靈堂內(nèi)的尸體忽然破棺而出衫樊,到底是詐尸還是另有隱情,我是刑警寧澤利花,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布科侈,位于F島的核電站,受9級(jí)特大地震影響炒事,放射性物質(zhì)發(fā)生泄漏兑徘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一羡洛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦欲侮、人聲如沸崭闲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)刁俭。三九已至,卻和暖如春韧涨,著一層夾襖步出監(jiān)牢的瞬間牍戚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工虑粥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留如孝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓娩贷,卻偏偏與公主長(zhǎng)得像第晰,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子彬祖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容