RecycleView 缓存学习记录

92 阅读3分钟

写缓存前先记录复用:(先缓存再复用

//上下滑动时进入的方法。fill 在分析复用和缓存时都比较重要
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
        RecyclerView.State state, boolean stopOnFocusable)
//复用时,取出 ViewHolder 的主要方法
layoutChunk(recycler, state, layoutState, layoutChunkResult);

列表的复用也就是对于 ViewHolder 的复用

//滑动列表时, item 的复用会走到此方法
tryGetViewHolderForPositionByDeadline()


第一个复用:
//复用第一个判断 changed scrap, 此 mState.isPreLayout() 判断对于动画而言的

// 0) If there is a changed scrap, try to find from there
if (mState.isPreLayout()) {
    holder = getChangedScrapViewForPosition(position);
    fromScrapOrHiddenOrCache = holder != null;
}
//进入此方法 也就是 ChangedScrap 的复用
--> getChangedScrapViewForPosition()
||
||
// 第一个重点 mChangedScrap ,获取可以复用的ViewHolder
--> ViewHolder holder = mChangedScrap.get(i);
// 第二个重点 hasStableIds(), 这是对于item设置的id, 当需要对每个item单独刷新时可设置此值,保证在刷新降低item闪动
--> mAdapter.hasStableIds()
第二个复用
// 如果第一个复用 没有找到 ViewHolder 值
if (holder == null){
        // 通过这个获取 ViewHolder,需要的是 postion
     --> getScrapOrHiddenOrCachedHolderForPosition(postion)
}
//第二个复用的主要方法
--> getScrapOrHiddenOrCachedHolderForPosition(postion)
  // 重点-: mAttachedScrap 先通过次 属性获取ViewHolder
  --> ViewHolder holder = mAttachedScrap.get(i);
  // 重点二:找隐藏的没有移出 的item, 取出view
  --> View view = mChildHelper.findHiddenNonRemovedView(position);
  // 重点三:从 mCachedViews 中取出可用的ViewHolder
  --> ViewHolder holder = mCachedViews.get(i);
第三个复用
// 对于有动画的item 而言; 当第二个复用依然没有获取到ViewHolder对象的时候,就利用 stableId 获取
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition)
    // 重点一: mAttachedScrap 获取ViewHolder
   --> ViewHolder holder = mAttachedScrap.get(i);
   // 重点二: mCachedViews 获取ViewHolder
   --> ViewHolder holder = mCachedViews.get(i);
第四个复用 (自定义实现)
// 对自定义 复用 ViewHolder; 对于缓存的话 也需要自己实现
View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);

第五个复用 (缓存池)
// 从 RecycledViewPool 中 获取 ScrapData 对象,再调用 scrapData.mScrapHeap;获取ViewHolder 的 list 数据
holder = getRecycledViewPool().getRecycledView(type);

第六步: 如果上面都没有找到能够使用ViewHolder,将会调用

//直接创建一个新的
holder = mAdapter.createViewHolder(RecyclerView.this, type);

onBindViewHolder 在什么时候调用?

//经历了以上 6步后ViewHolder一定存在对象,对ViewHolder 里面的控件对象赋值时,需要判断是否进入屏幕内
f (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
    // 重点: 此方法是 mAdapter.bindViewHolder(holder, offsetPosition);调用的时机
  --> bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}

那么复用在缓存后, 缓存是什么时候进行的呢?(回到fill方法,缓存在复用之前, 复用的是layoutChunk那么缓存recycleByLayoutState就是此方法)

//LineaLayoutManager 中的 fill()方法为开始
fill()
  //开始缓存
  --> recycleByLayoutState(recycler, layoutState);
   //recycleViewsFromEnd 和 recycleViewsFromStart 缓存时都调用了recycleChildren() 方法
   --> recycleViewsFromEnd(recycler, scrollingOffset, noRecycleSpace);
   --> recycler.recycleView(view)
   // 接收 ViewHolder, 然后进行
   --> recycleViewHolderInternal(ViewHolder holder)
   --> mCachedViews 的缓存
    //1: 第一:先判断 mCachedViews的缓存数,cachedViewSize >= mViewCacheMax && cachedViewSize > 0(mViewCacheMax = 2), 如果满足条件将移出CacheView 里面的 第0 条ViewHolder
    -->recycleCachedViewAt(0);
     //第二: 将移出的 ViewHolder, 添加到 RecycledViewPool 里面
     --> addViewHolderToRecycledViewPool(viewHolder, true);
      // 第三: 此方法 mScrap.get(viewType).mMaxScrap <= scrapHeap.size() (注:mMaxScrap = 5) 判断Pool 是否还能添加, 如果不能直接 return; 而且 pool 保存在 
      --> getRecycledViewPool().putRecycledView(holder);
     
    //2: RecycledViewPool 缓存)
   --> Pool 的缓存
    -->addViewHolderToRecycledViewPool(holder, true);

注意:

ArrayList<ViewHolder> mCachedViews 能缓存的长度 是 DEFAULT_CACHE_SIZE = 2

ArrayList<ViewHolder> scrapHeap  pool 能缓存的长度 是 DEFAULT_MAX_SCRAP = 5