环境
android sdk版本: 30
依赖:
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation "androidx.recyclerview:recyclerview:1.2.1"
复制代码
案例分析:
RecyclerView宽高固定;LayoutManager是LienarLayoutManager,vertical方向;数据20条,足以铺满整个屏幕。
现象:
①先创建Adapter,设置20条数据。
②调用RecyclerView#Adapter#notifyDataSetChanged方法后,当前页面中只有5个ViewHolder复用,其余的ViewHolder会走Adapter#createViewHolder方法创建新的ViewHolder。
原理:
为了搞清楚原理,我们先看一下,刚进入页面时,RecyclerView#Adapter#onCreateViewHolder方法的调用栈。
RecyclerView#Adapter#onCreateViewHolder方法的调用栈
RecyclerView#onLayout(): 4578行
RecyclerView#dispatchLayout(): 4012行
RecyclerView#dispatchLayoutStep2():4309行
LinearLayoutManager#onLayoutChildren(): 668行
LinearLayoutManager#fill(): 1591行
LinearLayoutManager#layoutChunk(): 1631行
LinearLayoutManager#LayoutState#next(): 2330行
RecyclerView#Recycler#getViewForPosition(int position): 6296行
RecyclerView#Recycler#getViewForPosition(position, boolean dryRun): 6300行
RecyclerView#Recycler#tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs): 6416行
RecyclerView#Adapter#createViewHolder(@NonNull ViewGroup parent, int viewType): 7295行
RecyclerView#Adapter#onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
复制代码
其核心是RecyclerView#Recycler#tryGetViewHolderForPositionByDeadline方法,它主要有两个作用,一个是获取ViewHolder;另一个给ViewHolder绑定数据。
获取ViewHolder是有顺序的,会先尝试从各级缓存里面去获取,会依次从Recycler scrap、cache、RecycledViewPool中获取,如果都获取不到,就直接创建一个ViewHolder。
RecyclerView#Recycler#tryGetViewHolderForPositionByDeadline
Attempts to get the ViewHolder for the given position, either from the Recycler scrap, cache, the RecycledViewPool, or creating it directly.
获取给定位置的ViewHolder。会依次从Recycler scrap、cache、RecycledViewPool中获取,如果都获取不到,就直接创建一个ViewHolder
核心:获取viewHolder;给viewHolder绑定数据。
复制代码
@Nullable
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
...
ViewHolder holder = null;
// 0) If there is a changed scrap, try to find from there
// 如果需要预先布局,就尝试从mChangedScrap中去获取。
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
...
}
// 1) Find by position from scrap/hidden list/cache
// 尝试依次从mAttachedScrap、mChildHelper的mHiddenViews、mCachedViews中去获取可复用的ViewHolder
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
// 校验holder是否有效,无效就清除vh
if (holder != null) {
if (!validateViewHolderForOffsetPosition(holder)) {
...
holder = null;
} else {
fromScrapOrHiddenOrCache = true;
}
}
}
if (holder == null) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
...
// 获取这个位置对应的数据类型,通过重写的Adapter#getItemViewType方法。
final int type = mAdapter.getItemViewType(offsetPosition);
// 2) Find from scrap/cache via stable ids, if exists
// 如果设置了stable ids,就根据id依次从mAttachedScrap、mCachedViews中查找
if (mAdapter.hasStableIds()) {
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
...
}
// 尝试从mViewCacheExtension中获取VH
if (holder == null && mViewCacheExtension != null) {
final View view = mViewCacheExtension
.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
...
}
}
// 尝试从 RecycledViewPool 中获取
if (holder == null) { // fallback to pool
...
holder = getRecycledViewPool().getRecycledView(type);
...
}
// 调用RecyclerView#Adapter#onCreateViewHolder生成ViewHolder
if (holder == null) {
...
holder = mAdapter.createViewHolder(RecyclerView.this, type);
...
}
}
...
// 给viewholder绑定数据
boolean bound = false;
if (mState.isPreLayout() && holder.isBound()) {
...
} else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
...
// 给viewholder绑定数据
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}
// 给holder.itemView设置RecyclerView#LayoutParams。并将其相互绑定。
...
return holder;
}
复制代码
实例分析。
我们这里调用RecyclerView#Adapter#notifyDataSetChanged方法后,既有复用的ViewHolder,也有新建的ViewHolder。复用的ViewHolder来自于哪里?为什么是5个?为什么还要新建ViewHolder?
带着这些问题,我们debug下我们的场景,看下ViewHolder的来源。
核心在于调用RecyclerView#Recycler#tryGetViewHolderForPositionByDeadline方法,关键在于下面这段代码:
holder = getRecycledViewPool().getRecycledView(type);
复制代码
我们知道,RecycledViewPool中是以viewType来存放不同的ViewHolder的,每个type最多存放五个。
所以我们在RecyclerView#Recycler#tryGetViewHolderForPositionByDeadline中,从RecycledViewPool中最多能找到五个可复用的ViewHolder,其余的只能走新建ViewHolder流程了。
RecycledViewPool
先来看下RecycledViewPool的说明:
RecycledViewPool lets you share Views between multiple RecyclerViews.
If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool and use setRecycledViewPool(RecyclerView.RecycledViewPool).
RecyclerView automatically creates a pool for itself if you don't provide one.
复制代码
大意是RecycledViewPool可以让你在多个recyclerview之间共享视图。
如果你想在RecyclerViews中回收视图,可以创建一个RecycledViewPool的实例并使用setRecycledViewPool(RecyclerView.RecycledViewPool)。
如果你不提供一个RecycledViewPool实例,那么RecyclerView会自动为自己创建一个。
我们看下RecyclerView#Recycler#getRecycledViewPool方法:确实是自动创建了一个。
RecycledViewPool getRecycledViewPool() {
if (mRecyclerPool == null) {
mRecyclerPool = new RecycledViewPool();
}
return mRecyclerPool;
}
复制代码
再看下RecycledViewPool的结构:
public static class RecycledViewPool {
private static final int DEFAULT_MAX_SCRAP = 5;
static class ScrapData {
final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
int mMaxScrap = DEFAULT_MAX_SCRAP;
long mCreateRunningAverageNs = 0;
long mBindRunningAverageNs = 0;
}
SparseArray<ScrapData> mScrap = new SparseArray<>();
...
}
复制代码
如上所示,里面有一个SparseArray<ScrapData>类型的变量mScrap,用来存储不同类型的ViewHolder。ScrapData数据中包含ArrayList<ViewHolder>类型变量mScrapHeap,用来存放具体的ViewHolder,它的最大容量是5(DEFAULT_MAX_SCRAP)。
RecycledViewPool中的数据何时添加的
本例中RecycledViewPool中的数据是从哪里添加的呢?
本例中,向ScrapData#mScrapHeap添加ViewHolder数据的调用链如下:
RecyclerView#onLayout(): 4578行
RecyclerView#dispatchLayout(): 4012行
RecyclerView#dispatchLayoutStep2():4309行
LinearLayoutManager#onLayoutChildren(): 633行
RecyclerView#LayoutManager#detachAndScrapAttachedViews(): 9493行
RecyclerView#LayoutManager#scrapOrRecycleView(): 9508行
RecyclerView#Recycler#recycleViewHolderInternal(): 6671行
RecyclerView#Recycler#addViewHolderToRecycledViewPool(): 6723行
RecyclerView#RecyclerViewPool#putRecycledView(ViewHolder scrap): 5931行
scrapHeap.add(scrap);
复制代码
核心是LinearLayoutManager#onLayoutChildren()方法中,如下的这段代码:
detachAndScrapAttachedViews(recycler);
复制代码
也就是说,在调用RecyclerView#Adapter#notifyDataSetChanged方法后,会触发绘制流程。在Linearlayout#layoutChildren方法中,会先对ViewHolder进行缓存,然后会对ViewHolder进行复用。

















![[02/27][官改] Simplicity@MIX2 ROM更新-一一网](https://www.proyy.com/wp-content/uploads/2020/02/3168457341.jpg)


![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)