前面我们完成了Handler和Binder的学习,可以说基础已经打好了。如果有不理解的同学,可以看看我的Framework专栏的其他文章,这里默认大家都是有这方面基础的了。
我们先来思考一个问题:invalidate会触发其他View的重绘吗?
答案很明显是会,但是为什么会呢?可能很多人就回答不上来了。还是来看系统源码吧!
打开我的sublime Text,搜索invalidate(),出来很多东西,然后右击,Goto definition。
(备注:sublime Text全局搜索 Find->Find Files)
我们最终会找到View的路径:frameworks/base/core/java/android/view/View.java
首先我们来看一下invalidate的英文是什么意思,一开始可能大家都会去猜这应该是刷新的意思,但是实际却是废弃的意思。
step1:
- public void invalidate() {
- invalidate(true);
- }
step2:
invalidateCache设置为true表示完全废弃缓存
- public void invalidate(boolean invalidateCache) {
- invalidateInternal(0, 0, mRight - mLeft,
- mBottom - mTop, invalidateCache, true);
- }
step3:
- 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.set(l, t, r, b);
- p.invalidateChild(this, damage);
意思就是把这块区域交给父布局去处理。
step4:
- @Deprecated
- @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) {
- // 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) != 0;
-
- // 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));
- }
-
- 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 = parent.invalidateChildInParent(location, dirty);//1
- 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);
- }
这里面是一个循环,就是如果还有父布局,就一直会调用注释1这个方法。最终会走到decorView。
step5:
这个方法是最上层ViewRootImpl里面的
- @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);//1
-
- return null;
- }
step6:
- 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();//1
- }
- }
step7:
- void scheduleTraversals() {
- if (!mTraversalScheduled) {
- mTraversalScheduled = true;
- mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
- mChoreographer.postCallback(
- Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable//1, null);
- if (!mUnbufferedInputDispatch) {
- scheduleConsumeBatchedInput();
- }
- notifyRendererOfFramePending();
- pokeDrawLockIfNeeded();
- }
- }
注意注释1处
step8:
- final class TraversalRunnable implements Runnable {
- @Override
- public void run() {
- doTraversal();//1
- }
- }
- final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
step9:
- void doTraversal() {
- if (mTraversalScheduled) {
- mTraversalScheduled = false;
- mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
-
- if (mProfile) {
- Debug.startMethodTracing("ViewAncestor");
- }
-
- performTraversals();//1
-
- if (mProfile) {
- Debug.stopMethodTracing();
- mProfile = false;
- }
- }
- }
step10:
- private void performTraversals() {
- ···
- performMeasure(childWidthMeasureSpec,childHeightMeasureSpec);
- ···
- performLayout(lp, mWidth, mHeight);
- ···
- performDraw();
-
- }
这个方法代码非常长,是不是似曾相识了!
总结一下:某一个View调用invalidate方法,回逐级向上调用直到ViewRootimpl,然后重新走绘制流程。
相信通过前面的开胃菜已经对View的invalidate有了一定的理解,那么我们再抛出一个问题:Activity如何如window与view进行分工合作的?
我们知道activity是通过window来管理view的,activity是管理生命周期的,view负责具体显示。
未完待续!