最近了解了一下RecycleView的缓存机制,做了一些记录,防止遗忘
一、RecyleView四级缓存
首先明确RecyecleView中缓存的对象是ViewHolder.
Recycler负责管理和缓存所有的ViewHolder。
RecycleView的缓存从上到下分为四层:scrap、cache、ViewCacheExtension、RecycleViewPool
1.1、 scrap
scrap 用来缓存正在显示的ViewHolder。
scrap 分为两个集合:mAttachedScrap 和 mChangedScrap。
- mAttachedScrap 用于缓存正在显示的ViewHolder
- mChangedScrap 用于缓存屏幕上发生变化的viewHolder,可能是数据发生了变化,也可能是ViewHolder类型发生了变化,mChangedScrap 中的ViewHolder会被移动到RecycledViewPool中。
当我们调用 notifyItemRangeChanged 方法的时候,会触发 requestLayout 方法,就会重新布局,重新布局的话,就会先将 viewHolder 放到 scrap 中(屏幕上变化的放入mChangedScrap 中,其余的放入mAttachedScrap 中),然后 fill 布局的时候,再从 mAttachedScrap 里面取出来直接使用。mChangedScrap 中的 viewHolder 会被移动到 RecycledViewPool 中,所以 mChangedScrap 对应的 item 需要从 pool 中取对应的 viewHolder,然后重新绑定。
View中的detach和remove
- detach 在ViewGroup中的实现很简单,只是将当前View从ParentView的ChildView数组中移除,将当前View的mParent设置为null, 可以理解为轻量级的临时remove。
- remove 代表真正的移除,不光从ChildView数组中移除,其他和View树各项联系也会被彻底斩断。
Recycled View中的Scrap View
- Scrap View指的是在RecyclerView中,经历了detach操作的缓存。此类缓存是通过position匹配的,不需要重新bindView。
- Recycled View指代的就是真正的移除操作remove后的缓存,取出时需重新bindView使用。
1.2、cached
数据结构mCachedViews,用于缓存从屏幕中移除,但是可能很快被再次显示的ViewHolder
- 它是一个 ArrayList 类型,不区分 viewHolder 的类型
- mCachedViews大小限制为2,但是你可以使用 setItemViewCacheSize()这个方法调整它的大小。
1.3、ViewCacheExtension
这个是需要自定义的,而且使用有很大的限制,所以不深入介绍了。
1.4、RecycledViewPool
RecycledViewPool 储存各个类型的 viewHolder 它缓存的是被恢复出厂设置的viewHolder,需要重新调用bind 绑定数据。
- RecycledViewPool 是按照ItemViewType 存储ViewHolder的,每种ItemViewType最大数量为5
- 可以通过 setMaxRecycledViews() 方法来设置每个类型储存的容量。
- 针对RecycleView嵌套的场景,如一个纵向的RecycleView 嵌套横向的RecycleView ,可以使用 setRecycledViewPool() 方法,公用RecycledViewPool
RecycleView滑出屏幕时的ViewHolder的复用过程
滚出屏幕的View会优先保存到mCacheViews, 如果mCacheViews中保存满了,就会保存到RecyclerViewPool中。
- 检查mCacheViews集合中是否还有空位,如果有空位,则直接放到mCacheViews集合
- 如果没有的话就把mCacheViews集合中最前面的ViewHolder拿出来放到RecyclerViewPool中,然后再把最新的这个ViewHolder放到mCacheViews集合
- 如果没有成功缓存到mCacheViews集合中,就直接放到RecyclerViewPool
二、Recycler 缓存加载流程
- scrap负责缓存屏幕中正在显示的ViewHolder,命中缓存后直接使用,不需要create和Bind
- 如果在 cache (mCachedViews)负责缓存刚刚移出屏幕,很可能被复用的ViewHolder。通过position获取,命中后不需create和bind
- ViewCacheExtension google预留的一个空的缓存,暂不讨论
- pool (RecycledViewPool )根据ViewType缓存ViewHolder, 用于缓存数据解绑后的ViewHolder,pool中命中的viewHolder 需要进行重新bind 进行数据绑定
- 如果所有缓存中都没有命中 viewHolder,会重新调用createViewHolder 和 bindViewHolder
三、一些优化方法
3.1、 setHastFixedSize
当知道Adapter内Item的改变不会影响RecyclerView宽高的时候,可以设置为true让RecyclerView避免重新计算大小。
注意两点:
- 当调用Adapter的增删改插方法,最后就会根据mHasFixedSize这个值来判断需要不需要requestLayout();
onItemRangeChanged(),
onItemRangeInserted(),
onItemRangeRemoved(),
onItemRangeMoved()
上面四个方法会调用triggerUpdateProcessor方法
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
会根据mHasFixedSize这个值来判断需要不需要requestLayout();
- 当调用Adapter的notifyDataSetChanged() 最后调用了onChanged,调用了requestLayout(),会去重新测量宽高。
public void onChanged() {
assertNotInLayoutOrScroll(null);
mState.mStructureChanged = true;
processDataSetCompletelyChanged(true);
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
3.2、setHasStableIds
Adapter.setHasStablesId(true)开启固定ID
DemoAdapter mAdapter=new DemoAdapter();
mAdapter.setHasStablesId(true);
在 Adapter 类中重写getItemId来给每个 Item 一个唯一的ID。
@Override
public long getItemId(int position){
return items.get(position).getId();
}
setHasStableIds(true)之后,数据为发生变化情况下,滚动recycleView
ViewHolder会被缓存到mAttachedScrap中,复用时通过position 从mAttachedScrap直接取出显示,不需要重新createViewHolder、bindViewHolder
用空间换时间,
从而规避滑动recyelveView过程中出现的闪烁问题。
3.3、recycleView 图片列表快速刷新
recyleView 中显示图片列表,快速滑动容易出现卡顿。一个优化思路,可以设置在滑动过程中暂停正在加载的图片,滑动停止之后再恢复图片的加载。
recyceView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
Glide.with(requireActivity()).resumeRequests()
} else {
Glide.with(requireActivity()).pauseRequests()
}
}
})
参考文章:
https://www.jianshu.com/p/1d2213f303fc
https://blog.csdn.net/weixin_43130724/article/details/90068112
https://www.jianshu.com/p/4a2b18135447
https://zhuanlan.zhihu.com/p/80475040
https://www.jianshu.com/p/aeb9ccf6a5a4
图片闪烁问题分析:
https://www.jianshu.com/p/29352def27e6