RecyclerView回收复用机制探索

10/3/2021 源码分析

# 缓存获取流程

# 入口一

onTouchEvent -> scrollByInternal -> scrollStep -> mLayout.scrollVerticallyBy -> scrollBy -> fill -> layoutChunk -> layoutState.next -> addView

layoutState.next -> recycler.getViewForPosition -> tryGetViewHolderForPositionByDeadline

tryGetViewHolderForPositionByDeadline内缓存复用详情

分几种情况去获取ViewHolder

  1. getChangedScrapViewForPosition -- mChangeScrap与动画相关

  2. getScrapOrHiddenOrCachedHolderForPosition -- 通过位置从mAttachedScrap和mCachedViews中获取ViewHolder

  3. getScrapOrCachedViewForId -- mAttachedScrap、mCachedViews(通过viewType和itemId)

  4. mViewCacheExtension.getViewForPositionAndType -- 自定义的缓存

  5. getRecycledViewPool().getRecycledView(type) -- 从缓存池里面获取

当没有缓存时mAdapter.createViewHolder

创建ViewHolder后绑定 -- tryBindViewHolderByDeadline -> mAdapter.bindViewHolder

# 入口二

onLayout -> dispatchLayout -> dispatchLayoutStep2 -> mLayout.onLayoutChildren->fill

# 四级缓存总结

  • mChangeScrap与mAttachedScrap

    用来缓存还在屏幕内的ViewHolder

  • mCachedViews(ArrayList 先进先出,最大不超过mViewCacheMax=2)

    用来缓存移除屏幕之外的ViewHolder

  • mViewCacheExtension

    开发给用户的自定义拓展缓存,需要用户自己管理View的创建和缓存

  • RecycledViewPool(SparseArray,key为viewType)

    ViewHolder缓存池

static class ScrapData {
  final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
  int mMaxScrap = DEFAULT_MAX_SCRAP; //5
  ...
}

# 回收机制流程

onLayout -> dispatchLayout -> dispatchLayoutStep2 -> mLayout.onLayoutChildren ->

# 情况一

fill -> recycleByLayoutState -> recycleViewsFromStart -> recycleChildren -> removeAndRecycleViewAt -> recycler.recycleView -> recycleViewHolderInternal

# 情况二

detachAndScrapAttachedViews -> scrapOrRecycleView

  1. -> recycler.recycleViewHolderInternal //缓存至mCacheViews、RecyclerViewPool

    a. -> holder没有变化且mCachedViews的数量大于mViewCacheMax -> recycleCachedViewAt -> addViewHolderToRecycledViewPool,mCachedViews.remove -> mCachedViews.add

    先进先出,先判断mCacheViews是否超过2,超过的话把index为0的放入缓存池,再插入新的holder

    b. -> addViewHolderToRecycledViewPool -> getRecycledViewPool().putRecycledView

public void putRecycledView(ViewHolder scrap) {
    final int viewType = scrap.getItemViewType();
    final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
    //超过最大值后直接丢弃
    if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
        return;
    }
    ...
    scrap.resetInternal(); //viewholder数据清空,只是类型,没有数据
    scrapHeap.add(scrap);
}
  1. -> recycler.scrapView
void scrapView(View view) {
    final ViewHolder holder = getChildViewHolderInt(view);
    if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
            || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
        if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
            throw new IllegalArgumentException("Called scrap view with an invalid view."
                    + " Invalid views cannot be reused from scrap, they should rebound from"
                    + " recycler pool." + exceptionLabel());
        }
        holder.setScrapContainer(this, false);
        mAttachedScrap.add(holder);
    } else {
        if (mChangedScrap == null) {
            mChangedScrap = new ArrayList<ViewHolder>();
        }
        holder.setScrapContainer(this, true);
        mChangedScrap.add(holder);
    }
}