ViewTreeObserver#addOnDrawListener的坑

在拿到布局后,有时需要给ViewTreeObserver注册一些监听器。今天就碰到一个注册OnDrawListener时发现的系统bug,记录一下。

问题描述

在设置布局之后,立即设置OnDrawListener

代码很简单:

textView.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
    @Override
    public void onDraw() {
    }
});

api26没有问题,api26以下竟然不会回调!!

源码排查

首先看下View#getViewTreeObserver

    public ViewTreeObserver getViewTreeObserver() {
        if (mAttachInfo != null) {
            return mAttachInfo.mTreeObserver;
        }
        if (mFloatingTreeObserver == null) {
            mFloatingTreeObserver = new ViewTreeObserver(mContext);
        }
        return mFloatingTreeObserver;
    }

刚得到View时,还没有mAttachInfo,所以此时返回mFloatingTreeObserver。随后在dispatchAttachedToWindow中:

    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mAttachInfo = info;
        ...
        if (mFloatingTreeObserver != null) {
            info.mTreeObserver.merge(mFloatingTreeObserver);
            mFloatingTreeObserver = null;
        }
        ...
}

注意这个merge方法,问题就出在这里。
看下api25的源码吧:

    void merge(ViewTreeObserver observer) {
        if (observer.mOnWindowAttachListeners != null) {
            if (mOnWindowAttachListeners != null) {
                mOnWindowAttachListeners.addAll(observer.mOnWindowAttachListeners);
            } else {
                mOnWindowAttachListeners = observer.mOnWindowAttachListeners;
            }
        }

        if (observer.mOnWindowFocusListeners != null) {
            if (mOnWindowFocusListeners != null) {
                mOnWindowFocusListeners.addAll(observer.mOnWindowFocusListeners);
            } else {
                mOnWindowFocusListeners = observer.mOnWindowFocusListeners;
            }
        }

        if (observer.mOnGlobalFocusListeners != null) {
            if (mOnGlobalFocusListeners != null) {
                mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners);
            } else {
                mOnGlobalFocusListeners = observer.mOnGlobalFocusListeners;
            }
        }

        if (observer.mOnGlobalLayoutListeners != null) {
            if (mOnGlobalLayoutListeners != null) {
                mOnGlobalLayoutListeners.addAll(observer.mOnGlobalLayoutListeners);
            } else {
                mOnGlobalLayoutListeners = observer.mOnGlobalLayoutListeners;
            }
        }

        if (observer.mOnPreDrawListeners != null) {
            if (mOnPreDrawListeners != null) {
                mOnPreDrawListeners.addAll(observer.mOnPreDrawListeners);
            } else {
                mOnPreDrawListeners = observer.mOnPreDrawListeners;
            }
        }

        if (observer.mOnTouchModeChangeListeners != null) {
            if (mOnTouchModeChangeListeners != null) {
                mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
            } else {
                mOnTouchModeChangeListeners = observer.mOnTouchModeChangeListeners;
            }
        }

        if (observer.mOnComputeInternalInsetsListeners != null) {
            if (mOnComputeInternalInsetsListeners != null) {
                mOnComputeInternalInsetsListeners.addAll(observer.mOnComputeInternalInsetsListeners);
            } else {
                mOnComputeInternalInsetsListeners = observer.mOnComputeInternalInsetsListeners;
            }
        }

        if (observer.mOnScrollChangedListeners != null) {
            if (mOnScrollChangedListeners != null) {
                mOnScrollChangedListeners.addAll(observer.mOnScrollChangedListeners);
            } else {
                mOnScrollChangedListeners = observer.mOnScrollChangedListeners;
            }
        }

        if (observer.mOnWindowShownListeners != null) {
            if (mOnWindowShownListeners != null) {
                mOnWindowShownListeners.addAll(observer.mOnWindowShownListeners);
            } else {
                mOnWindowShownListeners = observer.mOnWindowShownListeners;
            }
        }

        observer.kill();
    }

其他的监听器都merge了,唯独漏了OnDrawListener。不得不说这是一个悲伤的故事。。

修补措施

要解决这个问题,可以通过view.post去注册,因为这时,mAttachInfo已经有值了。

你可能感兴趣的:(ViewTreeObserver#addOnDrawListener的坑)