RecyclerView的ViewHolder复用流程

1,122 阅读1分钟

LayoutManager获取View是通过Recycler,所以它的缓存机制一定是在 Recycler

RecyclerView的布局流程设计

获取View的入口在getViewForPosition方法中:

# Recycler
public View getViewForPosition(int position) {
    return getViewForPosition(position, false);
}
​
View getViewForPosition(int position, boolean dryRun) {
  // ViewHolder.itemView
  return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}
ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
  // 0.from changed scrap
  if (mState.isPreLayout()) {
     holder = getChangedScrapViewForPosition(position);
     fromScrapOrHiddenOrCache = holder != null;
  }
  // 1.from scrap/hidden list/cache
  holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
  // 2.from scrap/cache via stable ids, if exists
  if (mAdapter.hasStableIds()) {
    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                            type, dryRun);
  }
  // 3.recyclerview pool
  holder = getRecycledViewPool().getRecycledView(type);
  // 4.create new viewholder
  holder = mAdapter.createViewHolder(RecyclerView.this, type);
}

0-3这四步会从“缓存”里面获取 ViewHolder

0.getChangedScrapViewForPosition -> mChangedScrap

mChangedScrap:存储数据变化的ViewHolder

ArrayList<ViewHolder> mChangedScrap = null;
ViewHolder getChangedScrapViewForPosition(int position) {
  //  mChangedScrap
  for (int i = 0; i < changedScrapSize; i++) {
     final ViewHolder holder = mChangedScrap.get(i);
     if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
         holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
         return holder;
     }
   }
   return null;
}

1.getScrapOrHiddenOrCachedHolderForPosition -> mAttachedScrap、ChildHelper.mHiddenViews、mCachedViews

  • mAttachedScrap:一次重新布局中,会暂时缓存所有的ViewHolder
  • mHiddenViews:暂存被隐藏的ItemView,比如做正再做离场动画的ItemView又因某种原因需要进场的时候被使用。
  • mCachedViews:真正意义上的一级缓存,之前的只是暂时存放,称为暂存区更为合理
ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
  // 1.attachedScrap
  for (int i = 0; i < scrapCount; i++) {
    final ViewHolder holder = mAttachedScrap.get(i);
    return holder;
    }
  // 2.ChildHelper.mHiddenViews
  View view = mChildHelper.findHiddenNonRemovedView(position);
  if (view != null) {
    ViewHolder vh = getChildViewHolderInt(view);
    return vh;
  }
  // 3.mCachedViews: first-level recycled view cache
  int cacheSize = mCachedViews.size();
  for (int i = 0; i < cacheSize; i++) {
    final ViewHolder holder = mCachedViews.get(i);
    return holder;
  }
  return null;
}

2.getScrapOrCachedViewForId -> mAttachedScrap、mCachedViews

调用该方法的前提是设置了mHasStableIds,才会走这段逻辑。

根据itemId而不是position,从mAttachedScrapmCachedViews中查找ViewHolder

// mAttachedScrap
ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
  final int count = mAttachedScrap.size();
  for (int i = count - 1; i >= 0; i--) {
    final ViewHolder holder = mAttachedScrap.get(i);
    if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
      return holder;
  }
  final int cacheSize = mCachedViews.size();
  for (int i = cacheSize - 1; i >= 0; i--) {
    final ViewHolder holder = mCachedViews.get(i);
    if (holder.getItemId() == id && !holder.isAttachedToTransitionOverlay()) {
      return holder;
    }
   return null;
}

思考:设置hasStableId有什么作用?

3.RecycledViewPool -> mScrap

如果以上缓存都找不到,说明只根据position已经无法找到ViewHolder了,就会根据ViewTypeRecycledViewPool中调用getRecycledView(type)寻找了。

# Recycler
holder = getRecycledViewPool().getRecycledView(type);# RecycledViewPool
final int type = mAdapter.getItemViewType(offsetPosition);
SparseArray<ScrapData> mScrap = new SparseArray<>();
public ViewHolder getRecycledView(int viewType) {
    final ScrapData scrapData = mScrap.get(viewType);
    if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
        final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
        for (int i = scrapHeap.size() - 1; i >= 0; i--) {
            if (!scrapHeap.get(i).isAttachedToTransitionOverlay()) {
                return scrapHeap.remove(i);
            }
        }
    }
    return null;
}
static class ScrapData {
   final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
   int mMaxScrap = DEFAULT_MAX_SCRAP;
   long mCreateRunningAverageNs = 0;
   long mBindRunningAverageNs = 0;
}

4.mAdapter.createViewHolder

如果缓存中都找不到,就只能调用mAdapter.createViewHolder去创建新的ViewHolder