社区精选|浅谈RecyclerView的性能优化
今天小编为大家带来的是社区作者 xuexiangjys 的文章,本文我们一起来看看RecyclerView的性能优化。
RecyclerView 的性能优化
在我们谈 RecyclerView 的性能优化之前,先让我们回顾一下 RecyclerView 的缓存机制。
Scrap 缓存:包括 mAttachedScrap 和 mChangedScrap,又称屏内缓存,不参与滑动时的回收复用,只是用作临时保存的变量。
mAttachedScrap:只保存重新布局时从 RecyclerView 分离的 item 的无效、未移除、未更新的 holder。 mChangedScrap:只会负责保存重新布局时发生变化的 item 的无效、未移除的 holder。
CacheView 缓存:mCachedViews 又称离屏缓存,用于保存最新被移除(remove)的 ViewHolder,已经和 RecyclerView 分离的视图,这一级的缓存是有容量限制的,默认最大数量为 2。 ViewCacheExtension:mViewCacheExtension 又称拓展缓存,为开发者预留的缓存池,开发者可以自己拓展回收池,一般不会用到。 RecycledViewPool:终极的回收缓存池,真正存放着被标识废弃(其他池都不愿意回收)的 ViewHolder 的缓存池。这里的 ViewHolder 是已经被抹除数据的,没有任何绑定的痕迹,需要重新绑定数据。
把屏幕上的 ViewHolder 与屏幕分离下来,存放到 Scrap 中,即发生改变的 ViewHolder 缓存到 mChangedScrap 中,不发生改变的 ViewHolder 存放到 mAttachedScrap 中。 剩下 ViewHolder 会按照 mCachedViews > RecycledViewPool 的优先级缓存到 mCachedViews 或者 RecycledViewPool 中。
先移除滑出屏幕的 item,第一级缓存 mCachedViews 优先缓存这些 ViewHolder。 由于 mCachedViews 最大容量为 2,当 mCachedViews 满了以后,会利用先进先出原则,把旧的 ViewHolder 存放到 RecycledViewPool 中后移除掉,腾出空间,再将新的 ViewHolder 添加到 mCachedViews 中。 最后剩下的 ViewHolder 都会缓存到终极回收池 RecycledViewPool 中,它是根据 itemType 来缓存不同类型的 ArrayList<ViewHolder>,最大容量为 5。
如果是预加载,则会先去 mChangedScrap 中精准查找(分别根据 position 和 id)对应的 ViewHolder。 如果没有就再去 mAttachedScrap 和 mCachedViews 中精确查找(先 position 后 id)是不是原来的 ViewHolder。 如果还没有,则最终去 mRecyclerPool 找,如果 itemType 类型匹配对应的 ViewHolder,那么返回实例,让它重新绑定数据。 如果 mRecyclerPool 也没有返回 ViewHolder 才会调用 createViewHolder()重新去创建一个。
在 mChangedScrap、mAttachedScrap、mCachedViews 中拿到的 ViewHolder 都是精准匹配。 mAttachedScrap 和 mCachedViews 没有发生变化,是直接使用的。 mChangedScrap 由于发生了变化,mRecyclerPool 由于数据已被抹去,所以都需要调用 onBindViewHolder()重新绑定数据才能使用。
RecyclerView 最多可以缓存 N(屏幕最多可显示的 item 数【Scrap 缓存】) + 2 (屏幕外的缓存【CacheView 缓存】) + 5*M (M 代表 M 个 ViewType,缓存池的缓存【RecycledViewPool】)。 RecyclerView 实际只有两层缓存可供使用和优化。因为 Scrap 缓存池不参与滚动的回收复用,所以 CacheView 缓存池被称为一级缓存,又因为 ViewCacheExtension 缓存池是给开发者定义的缓存池,一般不用到,所以 RecycledViewPool 缓存池被称为二级缓存。
首先在 notify 的时候,在 payload 中传入需要刷新的数据,一般使用 Bundle 作为数据的载体。 然后重写 RecyclerView.Adapter 的 onBindViewHolder(@NonNull RecyclerViewHolder holder, int position, @NonNull List<Object> payloads)方法
@Override
public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position, @NonNull List<Object> payloads) {
if (CollectionUtils.isEmpty(payloads)) {
Logger.e("正在进行全量刷新:" + position);
onBindViewHolder(holder, position);
return;
}
// payloads 为非空的情况,进行局部刷新
//取出我们在 getChangePayload()方法返回的 bundle
Bundle payload = WidgetUtils.getChangePayload(payloads);
if (payload == null) {
return;
}
Logger.e("正在进行增量刷新:" + position);
for (String key : payload.keySet()) {
if (KEY_SELECT_STATUS.equals(key)) {
holder.checked(R.id.scb_select, payload.getBoolean(key));
}
}
}
首先需要实现 DiffUtil.Callback 的 5 个抽象方法,具体可参考 DiffUtilCallback.java 然后调用 DiffUtil.calculateDiff 方法返回比较的结果 DiffUtil.DiffResult。 最后调用 DiffUtil.DiffResult 的 dispatchUpdatesTo 方法,传入 RecyclerView.Adapter 进行数据刷新。
((SimpleItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
关注公众号:拾黑(shiheibook)了解更多
赞助链接:
关注数据与安全,洞悉企业级服务市场:https://www.ijiandao.com/
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
随时掌握互联网精彩
- 寻找2024新风向,WIM创新者年会官宣!
- 庖丁解牛看节碳,年减碳12000吨背后的驱动力
- 特斯拉要把广告费省下来回馈客户?真有这好事?
- 回港上市的腾讯音乐,还会有巅峰期吗?
- 社区精选 |CSS 也可以写 if/else 语法了
- 想不到还有这种需求:在微信里备份自己?
- 美最新《人工智能研发合作与投资评估报告》旨在深化四国合作,弱化对华依赖
- APP卸载一年仍扣费2500多元,赶紧检查你的手机
- 中国移动吴冰:只有形成商业闭环 5G行业应用才能健康发展
- 一图读懂丨我国人工智能算力中心发展布局
- GitHub Copilot 更新:支持 PyCharm、IntelliJ IDEA 和 Neovim
- 统信软件当选广州市软件行业协会副会长单位