美文网首页androidAndroid实战Android
RecyclerView#Adapter使用中的两个陷阱

RecyclerView#Adapter使用中的两个陷阱

作者: sollian | 来源:发表于2019-04-30 11:14 被阅读848次

    其实就是Adapter中可以被覆写的两个方法

    1、onDetachedFromRecyclerView

    看下方法说明

            /**
             * Called by RecyclerView when it stops observing this Adapter.
             *
             * @param recyclerView The RecyclerView instance which stopped observing this adapter.
             * @see #onAttachedToRecyclerView(RecyclerView)
             */
            public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
            }
    

    在RecyclerView不再观察这个Adapter时被调用。
    与之对应的是onAttachedToRecyclerView

            /**
             * Called by RecyclerView when it starts observing this Adapter.
             * <p>
             * Keep in mind that same adapter may be observed by multiple RecyclerViews.
             *
             * @param recyclerView The RecyclerView instance which started observing this adapter.
             * @see #onDetachedFromRecyclerView(RecyclerView)
             */
            public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
            }
    

    在RecyclerView开始观察这个Adapter时,被调用。

    通常我们的理解是这样的:
    页面进入时,显示RecyclerView,调用onAttachedToRecyclerView,做一些注册工作;
    页面退出时,销毁RecyclerView,调用onDetachedFromRecyclerView,做一些解注册和其他资源回收的操作。

    而实际上,这两个方法的调用时机是:

        public void setAdapter(Adapter adapter) {
            // bail out if layout is frozen
            ...
            setAdapterInternal(adapter, false, true);
            ...
        }
    
        private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
                boolean removeAndRecycleViews) {
            if (mAdapter != null) {
                mAdapter.unregisterAdapterDataObserver(mObserver);
                mAdapter.onDetachedFromRecyclerView(this);//这里onDetachedFromRecyclerView
            }
            if (!compatibleWithPrevious || removeAndRecycleViews) {
                removeAndRecycleViews();
            }
            mAdapterHelper.reset();
            final Adapter oldAdapter = mAdapter;
            mAdapter = adapter;
            if (adapter != null) {
                adapter.registerAdapterDataObserver(mObserver);
                adapter.onAttachedToRecyclerView(this);//这里onAttachedToRecyclerView
            }
            if (mLayout != null) {
                mLayout.onAdapterChanged(oldAdapter, mAdapter);
            }
            mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
            mState.mStructureChanged = true;
        }
    

    可以看到,在调用setAdapter方法时,新设置的Adapter会调用onAttachedToRecyclerView,原有的Adapter会调用onDetachedFromRecyclerView
    所以如果覆写了onDetachedFromRecyclerView,为了确保被调用,需要在页面退出时,手动调用setAdapter(null)

    2、onViewDetachedFromWindow

    方法说明:

            /**
             * Called when a view created by this adapter has been detached from its window.
             *
             * <p>Becoming detached from the window is not necessarily a permanent condition;
             * the consumer of an Adapter's views may choose to cache views offscreen while they
             * are not visible, attaching and detaching them as appropriate.</p>
             *
             * @param holder Holder of the view being detached
             */
            public void onViewDetachedFromWindow(@NonNull VH holder) {
            }
    

    简言之,就是当itemView被从window上detach时调用。看起来很美好,与之对应的方法是:

            /**
             * Called when a view created by this adapter has been attached to a window.
             *
             * <p>This can be used as a reasonable signal that the view is about to be seen
             * by the user. If the adapter previously freed any resources in
             * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
             * those resources should be restored here.</p>
             *
             * @param holder Holder of the view being attached
             */
            public void onViewAttachedToWindow(@NonNull VH holder) {
            }
    

    然后我们也如第一个方法般调用了:
    onViewAttachedToWindow中做一些注册工作;
    onViewDetachedFromWindow中做一些解注册和释放资源的工作。

    在RecyclerView正常滚动时,这两个方法都会被调用。然而页面退出时,onViewDetachedFromWindow并不会被调用!

    追根溯源,会发现症结在LinearLayoutManager中:

        @Override
        public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
            super.onDetachedFromWindow(view, recycler);
            if (mRecycleChildrenOnDetach) {
                removeAndRecycleAllViews(recycler);
                recycler.clear();
            }
        }
    

    默认mRecycleChildrenOnDetach=false。我们需要调用setRecycleChildrenOnDetach(true)才能实现在页面退出时,依然调用onViewDetachedFromWindow方法。

    整合RecyclerView

    可以设计一个RecyclerView的基类,在基类中做如下处理:

    public class BaseRecyclerView extends RecyclerView implements LifecycleObserver {
        public BaseRecyclerView(@NonNull Context context) {
            super(context);
            init(context);
        }
    
        public BaseRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        public BaseRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context);
        }
    
        private void init(Context context) {
            if (context instanceof LifecycleOwner) {
                ((LifecycleOwner) context).getLifecycle().addObserver(this);
            }
        }
    
        @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        public void onDestory() {
            /*
             确保Adapter#onDetachedFromRecyclerView被调用
             */
            setAdapter(null);
        }
    
        @Override
        public void setLayoutManager(LayoutManager layoutManager) {
            super.setLayoutManager(layoutManager);
            if (layoutManager instanceof LinearLayoutManager) {
                /*
                确保Adapter#onViewDetachedFromWindow被调用
                 */
                ((LinearLayoutManager) layoutManager).setRecycleChildrenOnDetach(true);
            }
        }
    }
    
    

    相关文章

      网友评论

        本文标题:RecyclerView#Adapter使用中的两个陷阱

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