RecyclerView内部缓存Recycler

2019-04-26  本文已影响0人  vanzh

Recycler 只被RecyclerView持有。
Recycler 被回收的ViewHolder,实际上是放入了RecyclerViewPool里面。
Recycler 只会操作ViewHolder,不去操作具体的View,即使在某些情况下传入view,也是通过view去找到与之对应的holder。

Recycler的缓存分三级,一级缓存保存在列表的数据,第二级是根据使用者是否配置来确定,三级缓存则是缓存池。
一级缓存分两种,被废弃的view(Scrap View),被回收的View(RecycledView)

被废弃的view(Scrap View)缓存

mAttachedScrap:其添加holder的地方为scrapView(); 条件 可重用的holder已移除的holder失效的holder不是Update的holder
mChangedScrap:与mAttachedScrap同样在scrapView()里面添加数据,只要不是上述情况就加入到这里面;
以上两种方法都是在unscrapView()方法里面对缓存进行移除。判断条件为holder.mInChangeScrap.

被回收的View(RecycledView)缓存

mCachedViews:添加缓存的方法recycleViewHolderInternal(),根据holder 可回收或强制回收,同时要保证最大可缓存数大于0,holder的标志必须 是FLAG_INVALID、FLAG_REMOVED、FLAG_UPDATE、FLAG_ADAPTER_POSITION_UNKNOWN之一,则会加入到缓存列表mCachedViews;不符合上述条件,那么则直接加入到RecycledViewPool缓存池。要注意的是,虽然mCachedViews是ArrayList,但是它不是无限制了添加holder,有一个maxCachedSize对它做了限制。
而从mCachedViews里移除holder时,一般会将其添加到RecycledViewPool 回收池里。holder在加入缓存池的时候,holder会与所属的RecyclerView解除关联,还会将holder所有设置过的数据设置回初始状态,另一种移除的情况是加入到RecyclerView展示的情况。

那么RecyclerView 根据什么判定用废弃还是回收呢?
看看recycleViewHolderInternal 与 scrapView 各自调用的地方. 最后在LayoutManger里找到了关键代码,
RecyclerView.LayoutManger.scrapOrRecycleView(), 它调用链:onLayoutChildren()=> detachAndScrapAttachedViews() => scrapOrRecycleView(),
见下代码:

  private void scrapOrRecycleView(Recycler recycler, int index, View view) {
            final ViewHolder viewHolder = getChildViewHolderInt(view);
            ...
            if (viewHolder.isInvalid() && !viewHolder.isRemoved()
                    && !mRecyclerView.mAdapter.hasStableIds()) {
                removeViewAt(index);
                recycler.recycleViewHolderInternal(viewHolder);
            } else {
                detachViewAt(index);
                recycler.scrapView(view);
                mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
            }
        }

很明显,被回收的是:holder失效 && 未移除 && hasStableIds未设置为true,
其他情况,被废弃。
其上还有两个函数要关注一下:removeViewAt(index)、detachViewAt(index),他们的内部都由ChildHelpter去执行具体的操作,相同点都会从mBucket里将view移除,不同点在于:removeViewAt(index)还会将index的view从mHiddenViews移除的动作。 由于ChildHelper内部维护了可见\不可见两列表,而scrap方式不去删除不可见表的数据,因此其复用性要高一些,这也是后文获取缓存的缓存为什么scrap在前的原因。

关于缓存池 RecyclerViewPool

缓存池,保存holder根据getItemViewType()类别分别保存,它也是有存储个数限制的,默认每个type保存最大限制个数为5个。
mScrap:以type作为key 保存对应类型的缓存列表。
ScrapData:缓存列表的载体,mScrapHeap才是真正的列表。同时它还保存了缓存的最大时长。

获取缓存过程

前面分析了holder是怎么缓存的,下面看看它是怎么从各级缓存里面将数据拿出来的。
关键代码点:Recycler.tryGetViewHolderForPositionByDeadline()
查找缓存顺序

并不是这里找到了viewHolder就可以直接使用,需要对该holder设置一部分属性:
holder.mPreLayoutPosition,如果是预布局状态并且已绑定了view 才需要;
bound:holder未与RecyclerView、adapter绑定的情况,将mAdater与holder绑定,tryBindViewHolderByDeadline()。这种场景是从缓存池里取出的,因为在存入的时候是解除了关系并将holder设置回了初始状态,我们熟悉的ViewHolder.bindViewHolder()方法,在复用时就是这里调用的。

上一篇下一篇

猜你喜欢

热点阅读