美文网首页
ListView与ViewTreeObersever结合引起的内

ListView与ViewTreeObersever结合引起的内

作者: 倦飞知还 | 来源:发表于2021-09-09 16:07 被阅读0次

    结论:如果一个View作为ListView的Item,并且这个View在OnAttachToWindow与OnDetachFromWindow中进行ViewTreeObersever相关状态的注册与解注册,那么这个ListView所在的Fragment在销毁的时候,将会导致内存泄漏。

    原因:
    1.ListView的字view进行复用的时候,只会走attachToWindow而不会先走detachFromWindow,再走attachToWindow
    ListView 子View进行复用的代码
    堆栈

    android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3578)
    android.view.ViewGroup.addViewInner(ViewGroup.java:5442)
    android.view.ViewGroup.addViewInLayout(ViewGroup.java:5375)
    android.widget.ListView.setupChild(ListView.java:2172)
    android.widget.ListView.makeAndAddView(ListView.java:2093)
    android.widget.ListView.fillDown(ListView.java:799)
    android.widget.ListView.fillSpecific(ListView.java:1510)
    android.widget.ListView.layoutChildren(ListView.java:1808)
    android.widget.AbsListView.onLayout(AbsListView.java:2211)
    

    代码ViewGroup.addViewInLayout

    protected boolean addViewInLayout(View child, int index, LayoutParams params,
                boolean preventRequestLayout) {
            if (child == null) {
                throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
            }
            child.mParent = null;
            addViewInner(child, index, params, preventRequestLayout);
            child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
            return true;
        }
    

    因为ListView的子View复用的过程中没有涉及到View的删除,仅仅只是这个子View的index发生变化,所有并没有RemoveView的过程,没有走到detachFromWindow。复用的过程核心是addViewInLayout,会导致走到attachToWindow。

    1. ViewTreeObersever的监听所使用的容器都是CopyOnWriteArrayList
    public final class ViewTreeObserver {
        // Recursive listeners use CopyOnWriteArrayList
        private CopyOnWriteArrayList<OnWindowFocusChangeListener> mOnWindowFocusListeners;
        private CopyOnWriteArrayList<OnWindowAttachListener> mOnWindowAttachListeners;
        private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
        private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
        private CopyOnWriteArrayList<OnEnterAnimationCompleteListener>
                mOnEnterAnimationCompleteListeners;
    
        // Non-recursive listeners use CopyOnWriteArray
        // Any listener invoked from ViewRootImpl.performTraversals() should not be recursive
        private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
        private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
        private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners;
        private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners;
        private CopyOnWriteArray<OnWindowShownListener> mOnWindowShownListeners;
    

    CopyOnWriteArrayList与ArrayList存在以下差异是:
    1.ArrayList的一个item多次插入之后一次移除,就可以全部移除。

    1. CopyOnWriteArrayList 插入几次,就需要移除几次,才能全部移除。

    ArrayList.remove 一次移除所有都移除

    public boolean remove(Object o) {
            if (o == null) {
                for (int index = 0; index < size; index++)
                    if (elementData[index] == null) {
                        fastRemove(index);
                        return true;
                    }
            } else {
                for (int index = 0; index < size; index++)
                    if (o.equals(elementData[index])) {
                        fastRemove(index);
                        return true;
                    }
            }
            return false;
        }
    

    CopyOnWriteArrayList.remove,只移除了第一个

     public boolean remove(Object o) {
            Object[] snapshot = getArray();
            int index = indexOf(o, snapshot, 0, snapshot.length);
            return (index < 0) ? false : remove(o, snapshot, index);
        }
    

    结论:
    1.View的onAttachToWindow和onDetachFromWinwo并不是成对出现,大家要在里做注册与解注册监听,以及初始化和反初始化的时候要慎重,最好要用状态值控制。

    1. ViewTreeObersever的监听的注册与解注册要谨慎,要确保能够完成解注册。
    2. CopyOnWriteArrayList的remove与ArrayList的remove效果有差距,以后用到要谨慎。
      4.对于absListView的子类互相嵌套的情况要慎用。absListView本身在onAttachToWindow时要注册ViewTreeObersever的相关的监听。如上述,它它可能发生重复注册,导致释放不了。

    相关文章

      网友评论

          本文标题:ListView与ViewTreeObersever结合引起的内

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