美文网首页
Android 自定义View 源码解析 (一) invalid

Android 自定义View 源码解析 (一) invalid

作者: 是刘航啊 | 来源:发表于2020-12-10 17:59 被阅读0次

为什么调用 invalidate() 会重新执行 onDraw() ?

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    ...
    public void invalidate() {
        invalidate(true);
    }

    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 (p != null && ai != null && l < r && t < b) {
            final Rect damage = ai.mTmpInvalRect;
            damage.set(l, t, r, b);
            p.invalidateChild(this, damage);
        }
    }
    ...
}

View 「 invalidate() -> invalidate(true) -> invalidateInternal() 」最终调用到
ViewGroup 「 invalidateChild() 」

public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    ...
    public final void invalidateChild(View child, final Rect dirty) {
        ...
        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.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
                    view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
                }
            }

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

do while 循环调用 invalidateChildInParent(),里层的 View 调用的是
ViewGroup.invalidateChildInParent() 方法,最外层 View 也就是 ViewRootImpl,掉用 ViewRootImpl.invalidateChildInParent()

为什么在子线程更新 UI 会报错 ?
void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
    }
}
public ViewRootImpl(Context context, Display display, IWindowSession session,
            boolean useSfChoreographer) {
    ...
    mThread = Thread.currentThread();//主线程
    ...
}

mThread( 主线程 ) 在 ViewRootImpl 构造方法中初始化,当我们在子线程更新 UI 时,Thread.currentThread() 为子线程,所以在子线程更新 UI 会报错。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    ...
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
        checkThread();
        ...
        invalidateRectOnScreen(dirty);
    }

    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();
        }
    }
    
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    
   final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }
    
    private void performTraversals() {
        ...
        performMeasure();
        performLayout();
        performDraw();
        ...
    }
    
    private void performDraw() {
        ...
         boolean canUseAsync = draw(fullRedrawNeeded);
        ...
    }  
    
    private boolean draw(boolean fullRedrawNeeded) {
        ...
        if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
                        scalingRequired, dirty, surfaceInsets)) {
            return false;
        }
        ...
    }
    
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
        ...
        mView.draw(canvas);
        ...
    }
}

ViewRootImpl 「invalidateChildInParent() -> invalidateRectOnScreen() -> scheduleTraversals() -> doTraversal() -> performTraversals() -> performDraw() -> draw() -> drawSoftware()」最终调用到 View.draw()

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    ...
    public void draw(Canvas canvas) {
        ...
       drawBackground(canvas);
       ...
       onDraw(canvas);//绘制自己
       dispatchDraw(canvas);//绘制 children
    }
    ...
}
invalidate.jpg
invalidate 源码解析就到这里了,如果有什么写得不对的,可以在下方评论留言,我会第一时间改正。

相关文章

网友评论

      本文标题:Android 自定义View 源码解析 (一) invalid

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