RecyclerView相比较ListView先说多了多布局和缓存,目前已经在Android列表中大量普及使用,面试中也经常问到,所以对于RecyclerView的四级缓存机制也叫复用回收机制的分析很有必要(这部分很重要请@全村人来听)。
先说一下结论RecyclerView的四级缓存分别为:
- mChangeScrap与 mAttachedScrap 用来缓存还在屏幕内的 ViewHolder
- mCachedViews 用来缓存移除屏幕之外的 ViewHolder
- mViewCacheExtension 开发给用户的自定义扩展缓存,需要用户自己 管理 View 的创建和缓存
- RecycledViewPool ViewHolder 缓存池
本文将从从以下问题中逐步分析源码并寻找答案并验证结论:
1.RecyclerView回收(缓存)什么?复用什么?
答:回收和复用的都是ViewHolder,RecyclerView 的复用的主要方法tryGetViewHolderForPositionByDeadline()返回的是ViewHolder。同时几个回收(缓存)的主要方法返回的也是ViewHolder。
2.RecyclerView回收(缓存)到哪里去?从哪里获得复用?
1.先来看一下ViewHolder最终缓存地方RecyclerView.Recycler类中,本文的源码分析基于最新的AndroidX的RecyclerView(和以前的RecyclerView可能有点不一样,但是主要流程都是一致的)
public final class Recycler {
//一级缓存中用来存储屏幕中显示的ViewHolder
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
//二级缓存中用来存储屏幕外的ViewHolder
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
//暂可忽略 mAttachedScrap的不可变视图
private final List<ViewHolder>
mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
//mCachedViews屏幕外缓存的存储上限默认为DEFAULT_CACHE_SIZE也就是2,可变
int mViewCacheMax = DEFAULT_CACHE_SIZE;
//四级缓存当屏幕外缓存的大小大于2,便放入mRecyclerPool中缓存
RecycledViewPool mRecyclerPool;
//三级缓存自定义缓存,自己定义的缓存规则
private ViewCacheExtension mViewCacheExtension;
//默认屏幕外缓存大小
static final int DEFAULT_CACHE_SIZE = 2;
//...
2.再看一下主要的方法调用流程,从RecyclerView的onMeasure方法开始一直到三个存储的地方一级,二级和四级缓存,别问为啥没有mViewCacheExtension,问就是这个你需要自己去存
3.源码分析,本文对重点方法和重点代码进行分析(流程最好自己去跟一下)。
/**
* 5.RecyclerView.scrapOrRecycleView
*/
private void scrapOrRecycleView(Recycler recycler, int index, View view) {
final ViewHolder viewHolder = getChildViewHolderInt(view);
//只展示重点代码...
if (viewHolder.isInvalid() && !viewHolder.isRemoved()
&& !mRecyclerView.mAdapter.hasStableIds()) {
removeViewAt(index);
//这里调用到二级和四级缓存
recycler.recycleViewHolderInternal(viewHolder);
} else {
detachViewAt(index);
//这里调用到一级缓存
recycler.scrapView(view);
mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
}
}
① 分析一级缓存
/**
* 13.RecyclerView.scrapView
*/
void scrapView(View view) {
final ViewHolder holder = getChildViewHolderInt(view);
if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
//...
holder.setScrapContainer(this, false);
//缓存adapter其他notify系列方法(包括notifyDataSetChanged)被移除的ViewHolder
mAttachedScrap.add(holder);
} else {
if (mChangedScrap == null) {
mChangedScrap = new ArrayList<ViewHolder>();
}
holder.setScrapContainer(this, true);
//缓存adapter的notifyItemRangeChanged被移除的ViewHolder
mChangedScrap.add(holder);
}
}
② 分析二级缓存
/**
* 6.RecyclerView.recycleViewHolderInternal
*/
void recycleViewHolderInternal(ViewHolder holder) {
//...一系列是否需要二级回收的判断
if (forceRecycle || holder.isRecyclable()) {
if (mViewCacheMax > 0
&& !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_REMOVED
| ViewHolder.FLAG_UPDATE
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
// Retire oldest cached view
int cachedViewSize = mCachedViews.size();
//判断mCachedViews的大小是否大于2
if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
//重点分析一
recycleCachedViewAt(0);
cachedViewSize--;
}
//...计算targetCacheIndex的下标 让mCachedViews满足队列先进先出原则
mCachedViews.add(targetCacheIndex, holder);
cached = true;
}
if (!cached) {
//...如果二级缓存没有存储则添加到四级缓存
addViewHolderToRecycledViewPool(holder, true);
recycled = true;
}
} else {
//...
}
//...
}
/**
* 重点分析一:7.RecyclerView.recycleViewHolderInternal
* 作用:如果mCachedViews的大小大于2则内部调用addViewHolderToRecycledViewPool方法添加到RecycledViewPool中
*/
void recycleCachedViewAt(int cachedViewIndex) {
ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
//Viewholder存储到四级缓存
addViewHolderToRecycledViewPool(viewHolder, true);
//Viewholder在四级缓存存储后移除mCachedViews中对应的Viewholder
mCachedViews.remove(cachedViewIndex);
}
③分析四级缓存
/**
* 8.RecyclerView.recycleViewHolderInternal
*/
void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {
clearNestedRecyclerViewIfNotNested(holder);
//...
if (dispatchRecycled) {
dispatchViewRecycled(holder);
}
holder.mOwnerRecyclerView = null;
//存储到RecycledViewPool中
getRecycledViewPool().putRecycledView(holder);
}
/**
* 9.RecyclerView.recycleViewHolderInternal
*/
public void putRecycledView(ViewHolder scrap) {
final int viewType = scrap.getItemViewType();
final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
//每个类型 viewType 最多只能缓存5个viewHolder
if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
return;
}
//...
scrap.resetInternal();
//将viewHolder添加到RecycledViewPool.ScrapData.scrapHeap中
scrapHeap.add(scrap);
}
④ mCachedViews存储满了后(默认是2个)后,存储到RecycledViewPool中 ⑤ 最后:三种缓存最终都是存储到ArrayList中,(Bugly博客偷一张图😆 )
关于RecyclerView四级缓存复用机制二(复用ViewHolder)后面会分析,如果觉得这篇文章有用,欢迎点赞加收藏✨。
相关参考文章: RecyclerView源码解析(三)——深度解析缓存机制
本文转自 https://juejin.cn/post/6946100631669047333,如有侵权,请联系删除。