美文网首页
RecyclerView内部缓存Recycler

RecyclerView内部缓存Recycler

作者: vanzh | 来源:发表于2019-04-26 16:23 被阅读0次

    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()
    查找缓存顺序

    • 在预布局状态,如果有改变的, 就从 mChangeScrap里面找寻。
    • 没到找,根据位置position去找,从mAttachedScrap里面去找,没有还要去ChildHelper里面的不可见列表找,还找不到,去mCachedViews里面未失效找对应的位置的holder。
    • 没找到,根据adapter.getItemId(),再从mAttachedScrap,再从mCachedViews里找。
    • 还没找到,若配置了mViewCacheExtension对象,则从这里去找,一定会找到,找不到就会throw,若不配置则没有该步骤。
    • 还没找到,最后到缓存池里找,
    • 最后都没有找到,放弃治疗了,自己去创建一个viewHolder。

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

    相关文章

      网友评论

          本文标题:RecyclerView内部缓存Recycler

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