RecyclerView回收复用机制探索
# 缓存获取流程
# 入口一
onTouchEvent -> scrollByInternal -> scrollStep -> mLayout.scrollVerticallyBy -> scrollBy -> fill -> layoutChunk -> layoutState.next -> addView
layoutState.next -> recycler.getViewForPosition -> tryGetViewHolderForPositionByDeadline
tryGetViewHolderForPositionByDeadline内缓存复用详情
分几种情况去获取ViewHolder
getChangedScrapViewForPosition -- mChangeScrap与动画相关
getScrapOrHiddenOrCachedHolderForPosition -- 通过位置从mAttachedScrap和mCachedViews中获取ViewHolder
getScrapOrCachedViewForId -- mAttachedScrap、mCachedViews(通过viewType和itemId)
mViewCacheExtension.getViewForPositionAndType -- 自定义的缓存
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
-> 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);
}
- -> 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);
}
}