美文网首页
invalidate方法源码追踪

invalidate方法源码追踪

作者: 陆元伟 | 来源:发表于2018-05-05 23:27 被阅读0次

    首先查看

    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里面调用了ViewParent的invaludateChild方法,而ViewParent就是当前View的外层ViewGroup
    查看ViewGroup的invaludateChild方法.这个方法里面有个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 view为DecorView
    当我们setContent方法后,在Activity 的onResume方法前,会把当前的顶层DecoView添加到addView 到WindowManager里面,而WindowManager的实现类WindowManagerImpl里面的调用的是WindowManagerGlobal的addView方法,如下。看注释的关系代码,这里实例化了一个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方法,其中view为DecorView,在该方法里面调用了view.assigenParent(this),把ViewRootImpl设置为DecorView的ViewParent.

     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();
                   
    //关键代码.设置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);
                    }
    
                 
            }
        }
    

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

     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方法->ViewRootImpl的invalidateChild---->performTraversals->View onDraw,大概主要调用流程如此,

    相关文章

      网友评论

          本文标题:invalidate方法源码追踪

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