RecycleView的复用、优化、缓存

1、RecycleView四级缓存:

 

1.1、从上图中看出:RecycleView的缓存从上到下分为四层:scrap、cache、ViewCacheExtension、RecycleViewPool

1.2、RecycleView缓存的对象是ViewHolder,而RecycleView复用的对象也是ViewHolder

1.3、Recycler负责管理和缓存所有的ViewHolder。

2、四级缓存介绍:

2.1、scrap:

从图中可以看出,scrap是用来缓存正在显示的ViewHolder

scrap分为两个集合:mAttachedScrap和mChangedScrap

  • mAttachedScrap用来缓存正在显示的ViewHolder
  • mChangedScrap用来缓存屏幕上发生变化的ViewHolder,也可能是ViewHolder类型发生了变化,mChangedScrap中的ViewHolder会被移动到RecycleViewPool中

2.2、cached:

数据结构mCacheViews,用来缓存从屏幕中移除,但是可能很快被再次显示的ViewHolder

  • 它是一个ArrayList类型,不区分ViewHolder的类型
  • mCacheViews大小限制为2,但是可以使用setItemViewCacheSized()这个方法调整它的大小

2.3、ViewCacheExtension:

这个是需要自定义的,而且使用有很大的限制,所以不深入介绍

2.4、RecycleViewViewPool:

RecycledViewPool 储存各个类型的 viewHolder 它缓存的是被恢复出厂设置的viewHolder,需要重新调用bind 绑定数据。

  • RecycledViewPool 是按照ItemViewType 存储ViewHolder的,每种ItemViewType最大数量为5
  • 可以通过 setMaxRecycledViews() 方法来设置每个类型储存的容量。
  • 针对RecycleView嵌套的场景,如一个纵向的RecycleView 嵌套横向的RecycleView ,可以使用 setRecycledViewPool() 方法,公用RecycledViewPool

RecycleView滑出屏幕时的ViewHolder的复用过程

 

滚出屏幕的View会优先保存到mCacheViews, 如果mCacheViews中保存满了,就会保存到RecyclerViewPool中。

  • 检查mCacheViews集合中是否还有空位,如果有空位,则直接放到mCacheViews集合
  • 如果没有的话就把mCacheViews集合中最前面的ViewHolder拿出来放到RecyclerViewPool中,然后再把最新的这个ViewHolder放到mCacheViews集合
  • 如果没有成功缓存到mCacheViews集合中,就直接放到RecyclerViewPool

3、Recycler 缓存加载流程

 

  • scrap负责缓存屏幕中正在显示的ViewHolder,命中缓存后直接使用,不需要create和Bind
  • 如果在 cache (mCachedViews)负责缓存刚刚移出屏幕,很可能被复用的ViewHolder。通过position获取,命中后不需create和bind
  • ViewCacheExtension google预留的一个空的缓存,暂不讨论
  • pool (RecycledViewPool )根据ViewType缓存ViewHolder, 用于缓存数据解绑后的ViewHolder,pool中命中的viewHolder 需要进行重新bind 进行数据绑定
  • 如果所有缓存中都没有命中 viewHolder,会重新调用createViewHolder 和 bindViewHolder

4、RecyclerView的部分优化

1.在 onCreateViewHolder 里面写点击事件
这个很好理解,onBindViewHolder会多次调用

2.LinearLayoutManager.setInitialPrefetchItemCount
注意,此方法LinearLayoutManager专属

    public void setInitialPrefetchItemCount(int itemCount) {
        mInitialItemPrefetchCount = itemCount;
    }

mInitialItemPrefetchCount 在这里用到

    @Override
    public void collectInitialPrefetchPositions(int adapterItemCount,
            LayoutPrefetchRegistry layoutPrefetchRegistry) {
        final int direction = fromEnd
                ? LayoutState.ITEM_DIRECTION_HEAD
                : LayoutState.ITEM_DIRECTION_TAIL;
        int targetPos = anchorPos;
        for (int i = 0; i < mInitialItemPrefetchCount; i++) {
            if (targetPos >= 0 && targetPos < adapterItemCount) {
                layoutPrefetchRegistry.addPosition(targetPos, 0);
            } else {
                break; // no more to prefetch
            }
            targetPos += direction;
        }
    }

说白了就是预取操作。collectInitialPrefetchPositions定义当此LayoutManager的RecyclerView嵌套在另一个RecyclerView中时应预取多少内部项目。也就是说只有RecyclerView嵌套才会生效,否则无效

3.setHasFixedSize
setHasFixedSize 会给 mHasFixedSize 赋值,看一下用到的地方

// 第一处
    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        if (mLayout == null) {
            defaultOnMeasure(widthSpec, heightSpec);
            return;
        }
        if (mLayout.mAutoMeasure) {
            ......
        } else {
            if (mHasFixedSize) {
                mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
                return;
            }
            // custom onMeasure
            if (mAdapterUpdateDuringMeasure) {
                eatRequestLayout();
                onEnterLayoutOrScroll();
                processAdapterUpdatesAndSetAnimationFlags();
                onExitLayoutOrScroll();

                if (mState.mRunPredictiveAnimations) {
                    mState.mInPreLayout = true;
                } else {
                    // consume remaining updates to provide a consistent state with the layout pass.
                    mAdapterHelper.consumeUpdatesInOnePass();
                    mState.mInPreLayout = false;
                }
                mAdapterUpdateDuringMeasure = false;
                resumeRequestLayout(false);
            }

            if (mAdapter != null) {
                mState.mItemCount = mAdapter.getItemCount();
            } else {
                mState.mItemCount = 0;
            }
            eatRequestLayout();
            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
            resumeRequestLayout(false);
            mState.mInPreLayout = false; // clear
        }
    }
// 第二处
    private class RecyclerViewDataObserver extends AdapterDataObserver {

        void triggerUpdateProcessor() {
            if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
                RecyclerView.this.postOnAnimation(mUpdateChildViewsRunnable);
            } else {
                mAdapterUpdateDuringMeasure = true;
                requestLayout();
            }
        }
    }

mLayout.mAutoMeasure 默认是false,当然也可以自己设置。当mHasFixedSize是true的时候只会执行onMeasure。另外,每次notifyDataSetChanged的时候只会mUpdateChildViews而不用整个requestLayout。当然,有个前提,adapter的数据变化不会导致RecycleView的大小变化 。
总结,当RecycleView大小固定的时候,setHasFixedSize 为true可以使每次onMeasure和notifyDataSet的时候不用去request整个layout

4.多个RecycleView共用RecycledViewPool
RecycledViewPool 其实并不陌生,就是缓存机制的最后一个pool。如果这里还取不到view就重新create。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值