社区精选|浅谈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/

随时掌握互联网精彩
- 微软电脑管家3.15版正式发布:新增一键清理网络痕迹 防止隐私泄露
- 港币对人民币汇率2023年8月1日
- 社区精选|手把手教大家在 gRPC 中使用 JWT 完成身份校验
- 苹果iOS 16.1.2正式发布,这些重点功能已优化!
- 从预测到决策,九章云极DataCanvas 推出 YLearn 因果学习开源项目
- 人像体验“质变”,vivo S12 Pro自拍实测
- 手机内存扩展压力测试,竟直接测崩了!
- 【周末荐书】自私的人类:人类如何避免自我毁灭
- FCC敲定19亿美元华为中兴设备在美拆除和更换计划
- 【杂谈快报】高通发布骁龙 888 Plus 5G 移动平台,相关终端 Q3 上市
- 河北工信厅副厅长徐科华:深化新一代信息技术与制造业融合 加快传统产业数字化转型和高质量发展
- 有令不行严厉处置!APP侵权专项整治在行动