缓存层级(缓存结构/缓存类型)
- ScrapView(脏视图):
- 布局期间进入临时分离状态的子视图
- 可以重复使用,而不会与父级RecyclerView完全分离,如果不需要重新绑定,则不进行修改如果视图被视为脏,则由适配器修改。
- RV展示成功后,Scrap这层的缓存就为空了,在从Scrap中取视图的同时就被移出了缓存
这里的脏怎么理解呢?就是指那些在展示之前必须重新绑定的视图,比如一个视图原来展示的是“张三”,之后需要展示“李四”了,那么这个视图就是脏视图,需要重新绑定数据后再展示的。
- RecyclerView(回收视图):
- 先前用于显示适配器特定位置的数据的视图可以放置在高速缓存中以供稍后重用再次显示相同类型的数据
这可以通过跳过初始布局或构造来显着提高性能
缓存变量
Recycler中的缓存变量
- mAttachedScrap
- Scrap中的一种,这里的数据是不做修改的,不会重新走Adapter的绑定方法
- 没有容量限制的,只要符合条件的我来者不拒,全收了
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
- mChangedScrap
- Scrap中的一种
- 存放的是发生了变化的ViewHolder,如果使用到了这里的缓存的ViewHolder是要重新走Adapter的绑定方法的。
ArrayList<ViewHolder> mChangedScrap = null;
- mCachedViews
- 属于Recycler(View),
- remove掉的视图,已经和RV分离的关系的视图,但是它里面的ViewHolder依然保存着之前的信息,比如position、和绑定的数据等等
- 缓存是有容量限制的,默认是2(不同版本API可能会有差异)
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
- mViewCacheExtension
- 开发者自由发挥的,官方并没有默认实现,它本身是null。
private ViewCacheExtension mViewCacheExtension;
- mRecyclerPool
- 保存的ViewHolder不仅仅是removed掉的视图,而且是恢复了出厂设置的视图,任何绑定过的痕迹都没有了,想用这里缓存的ViewHolder那是铁定要重新走Adapter的绑定方法了
- 按照itemType来分开存储的
RecycledViewPool mRecyclerPool;
ChildHelper中的缓存变量
- mHiddenViews
- 缓存被隐藏的ViewHolder
final List<View> mHiddenViews = new ArrayList<View>();
RecyclerView的复用流程
最终调用到Recycler的tryGetViewHolderForPositionByDeadline方法
//第一步
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
//第二步
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
//分三个流程
1.从mAttachedScrap中查找
2.ChildHelper类中的mHiddenViews中查找
3.从mCachedViews中查找的
}
//第三步
if (holder == null && mViewCacheExtension != 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 = mAdapter.createViewHolder(RecyclerView.this, type);
}
//最后
if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
...
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
//最终调用 mAdapter.bindViewHolder(holder, offsetPosition);
...
}
- 第一步:getChangedScrapViewForPosition(position)方法中找需要的视图,但是有个条件mState.isPreLayout()要为true,这个一般在我们调用adapter的notifyItemChanged等方法时为true,其实也很好理解,数据发生了变化,viewholder被detach掉后缓存在mChangedScrap之中,在这里拿到的viewHolder后续需要重新绑定
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
- 第二步:如果没有找到视图则从getScrapOrHiddenOrCachedHolderForPosition这个方法中继续找。
- 首先从mAttachedScrap中查找
- 再次从前面略过的ChildHelper类中的mHiddenViews中查找
- 最后是从mCachedViews中查找的
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
}
ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
//1.从mAttachedScrap中查找
for (int i = 0; i < scrapCount; i++) {
final ViewHolder holder = mAttachedScrap.get(i);
return holder;
}
//2.ChildHelper类中的mHiddenViews中查找
if (!dryRun) {
//最终调用 final View view = mHiddenViews.get(i);
View view = mChildHelper.findHiddenNonRemovedView(position);
if(view != null) {
final ViewHolder vh = getChildViewHolderInt(view);
return vh;
}
}
//3.mCachedViews中查找
final int cacheSize = mCachedViews.size();
for (int i = 0; i < cacheSize; i++) {
final ViewHolder holder = mCachedViews.get(i);
return holder
}
}
- 第三步:mViewCacheExtension中查找,我们说过这个对象默认是null的,是由我们开发者自定义缓存策略的一层,所以如果你没有定义过,这里是找不到View的
if (holder == null && mViewCacheExtension != null) {
...
final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
}
...
}
- 第四步:从RecyclerPool中查找,前面我们介绍过RecyclerPool,先通过itemType从SparseArray类型的mscrap中拿到ScrapData,不为空继续拿到scrapHeap这个ArrayList,然后取到视图,这里拿到的视图需要重新绑定
if (holder == null) {
holder = getRecycledViewPool().getRecycledView(type);
}
- 第五步:创建ViewHolder
if (holder == null) {
holder = mAdapter.createViewHolder(RecyclerView.this, type);
}
- 最后:绑定ViewHolder
if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
...
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
...
}
private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition, int position, long deadlineNs) {
...
mAdapter.bindViewHolder(holder, offsetPosition);
...
}
RecyclerView的回收流程
。。。