RecyclerView 通过多级缓存(Scrap、Cache、ViewCacheExtension 和
RecycledViewPool)来最小化 View 创建和数据绑定的开销。
RecyclerView 的缓存由 Recycler
类管理,它是 RecyclerView
的内部成员。Recycler
使用以下字段存储不同级别的缓存:
1 2 3 4 5
| final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>(); ArrayList<ViewHolder> mChangedScrap = null; final ArrayList<ViewHolder> mCachedViews = new ArrayList<>(); ViewCacheExtension mViewCacheExtension; final RecycledViewPool mRecyclerPool = new RecycledViewPool();
|
- mAttachedScrap:存储仍附加到 RecyclerView 但临时不可见的
ViewHolder(例如动画或布局变更中)。
- mChangedScrap:类似 mAttachedScrap,但用于数据变更的
ViewHolder(例如使用 AdapterHelper)。
- mCachedViews:快速缓存,默认大小 2(可通过
setItemViewCacheSize(int)
调整),存储最近移除的
ViewHolder,保留绑定状态。
- mViewCacheExtension:可选的开发者自定义缓存(通过
setViewCacheExtension()
设置)。
- mRecyclerPool:回收池,按 ViewType 分组存储重置后的
ViewHolder,默认每个类型 5 个(可通过
setMaxRecycledViews(int viewType, int max)
调整)。
这些字段形成了缓存的层次:Scrap(最高优先级,零开销复用)→ Cache →
Extension → Pool → 新创建。
ViewHolder
获取流程(tryGetViewHolderForPositionByDeadline 方法)
当 RecyclerView 需要一个 ViewHolder 时(例如在布局填充中),它调用
Recycler.getViewForPosition(int position)
,后者委托给
tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs)
。这个方法是缓存机制的核心,按优先级从缓存中查找
ViewHolder,如果找不到则创建新实例。以下是简化后的源码逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
ViewHolder holder = null; if (mState.isPreLayout()) { holder = getChangedScrapViewForPosition(position); } if (holder == null) { holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun); }
if (holder == null) { final int offsetPosition = mAdapterHelper.findPositionOffset(position); final int type = mAdapter.getItemViewType(offsetPosition);
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition), type, dryRun); if (holder != null) { if (!validateViewHolderForOffsetPosition(holder, offsetPosition, dryRun)) { if (!dryRun) { holder.addFlags(ViewHolder.FLAG_INVALID); if (holder.isScrap()) { removeDetachedView(holder.itemView, false); holder.unScrap(); } else if (holder.wasReturnedFromScrap()) { holder.clearReturnedFromScrapFlag(); } recycleViewHolderInternal(holder); } holder = null; } else { fromScrapOrHiddenOrCache = true; } } }
if (holder == null && viewCacheExtension != null) { final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type); if (view != null) { holder = getChildViewHolder(view); } }
if (holder == null) { holder = getRecycledViewPool().getRecycledView(type); if (holder != null) { holder.resetInternal(); if (FORCE_INVALIDATE_DISPLAY_LIST) { invalidateDisplayListInt(holder); } } }
if (holder == null) { long start = getNanoTime(); if (deadlineNs != FOREVER_NS && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) { return null; } holder = mAdapter.createViewHolder(RecyclerView.this, type); if (ALLOW_THREAD_GAP_WORK) { RecyclerView innerView = findNestedRecyclerView(holder.itemView); if (innerView != null) { holder.mNestedRecyclerView = new WeakReference<>(innerView); } } long end = getNanoTime(); mRecyclerPool.factorInCreateTime(type, end - start); }
return holder; }
|
流程解释: 1. Scrap 优先:先从 mAttachedScrap
或
mChangedScrap
中查找(通过
getChangedScrapViewForPosition
和
getScrapOrHiddenOrCachedHolderForPosition
)。Scrap 中的
ViewHolder 无需重置,直接复用,适合动画或快速滑动场景。 2. Cache
检查:如果 Scrap 为空,从 mCachedViews
中找匹配 ID 或类型的
ViewHolder(getScrapOrCachedViewForId
)。如果找到,验证位置有效性;无效则回收。
3. Extension(可选):调用自定义的
ViewCacheExtension.getViewForPositionAndType
。 4. Pool
回收:从 mRecyclerPool
获取(getRecycledView(type)
),并调用
holder.resetInternal()
重置状态(清除标志、数据等)。 5.
新创建:调用
Adapter.createViewHolder
,并记录创建时间以优化未来决策(考虑到
deadline 超时)。
这个方法支持 “dry run” 模式(不实际移除 ViewHolder)和
deadline(纳秒级超时),用于预取或并发优化。
ViewHolder
回收流程(recycleViewHolderInternal 方法)
当 ViewHolder 不再需要时(例如滑动出屏),RecyclerView 调用
recycleViewHolderInternal(ViewHolder holder)
将其放入适当缓存。源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| void recycleViewHolderInternal(ViewHolder holder) {
boolean cached = false; boolean recycled = false;
if (shouldBeKeptAsScrap(holder) || holder.itemView.getParent() != null) { if (transientStatePreventsRecycling || holder.isTmpDetached()) { scrapView(holder.itemView); cached = true; } } else if (mViewCacheMax <= 0 || forceCache || holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_UPDATE)) { addViewHolderToRecycledViewPool(holder, true); recycled = true; } else { if (mCachedViews.size() >= mViewCacheMax && !mCachedViews.isEmpty()) { recycleCachedViewAt(0); } mCachedViews.add(holder); cached = true; }
if (!cached && !recycled && holder.isRecyclable()) { mRecyclerPool.putRecycledView(holder); recycled = true; } }
|
回收逻辑解释: - Scrap:如果 holder
有临时状态(isTmpDetached()
)或仍在父视图中,放入
mAttachedScrap
(通过 scrapView
)。 -
Cache:如果 Cache
未满(mCachedViews.size() < mViewCacheMax
),直接添加。Cache
满时,移除最早的(recycleCachedViewAt(0)
)并移到 Pool。 -
Pool:调用 mRecyclerPool.putRecycledView(holder)
,按
ViewType 存储。如果 Pool 满(默认 5),丢弃最旧的。进入 Pool 前,调用
Adapter.onViewRecycled(holder)
释放资源。 -
特殊处理:无效或移除的 holder 直接回收到 Pool,避免污染 Cache。
相关辅助方法: -
addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled)
:重置
holder 并放入 Pool,同时调用 onViewRecycled
。 -
RecycledViewPool.putRecycledView(ViewHolder scrap)
:内部使用
SparseArray<ArrayList> 按类型存储。
RecycledViewPool 的实现
RecycledViewPool
是静态可共享的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public static class RecycledViewPool { private SparseArray<ScrapData> mScrap = new SparseArray<>(); private int mAttachCountForPoolingContainer = 0;
static class ScrapData { final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>(); int mMaxScrap = DEFAULT_MAX_SCRAP; }
public void putRecycledView(ViewHolder scrap) { final int viewType = scrap.getItemViewType(); final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap; if (getScrapDataForType(viewType).mMaxScrap <= scrapHeap.size()) { return; } scrap.resetInternal(); scrapHeap.add(scrap); }
public ViewHolder getRecycledView(int viewType) { final ScrapData scrapData = getScrapDataForType(viewType); if (!scrapData.mScrapHeap.isEmpty()) { final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap; return scrapHeap.remove(scrapHeap.size() - 1); } return null; } }
|
- 按类型管理:使用 SparseArray 存储每个 ViewType 的 ScrapData,每个
ScrapData 有 mScrapHeap (ArrayList) 和 mMaxScrap。
- 放入/取出:LIFO(后进先出)原则,放入前重置 holder。
性能优化与注意事项
- 缓存大小调整:
setItemViewCacheSize(int size)
修改
mRequestedCacheMax
,影响 mCachedViews。
- 共享 Pool:多个 RecyclerView 可共享同一个
Pool(
setRecycledViewPool
),减少内存。
- 预取与超时:
tryGetViewHolderForPositionByDeadline
支持
deadlineNs,防止主线程阻塞(结合 GapWorker 预取)。
- 常见问题:源码中多处校验标志(如 FLAG_INVALID),防止回收无效
holder。开发者需在
onViewRecycled
释放资源,避免泄漏。
- DiffUtil 集成:数据更新时,使用 mChangedScrap 处理变更
ViewHolder。
总结
RecyclerView 的缓存机制通过 Recycler
的多级结构高效工作:Scrap 用于即时复用,Cache 保留绑定状态,Extension
自定义,Pool
作为最后储备。核心方法tryGetViewHolderForPositionByDeadline
按序查找,recycleViewHolderInternal
智能回收。这种设计使
RecyclerView 在长列表中保持流畅,开发者可通过 API
调整以适应具体场景。