Room查询联系人过慢:排查后为RecyclerView加载2000条数据耗时高,如何优化?
问题排查与解决方案
一、初始数据库查询阶段问题排查
你当前的2000条数据的联系人表结构非常简单,正常查询耗时应该在毫秒级,出现6~8秒的耗时存在以下可优化的操作点:
- 实体类重写的
toString()方法不合理
你在LocalContactsUserEntity中重写的toString方法调用了Gson序列化:
@Entity(tableName = "Contacts") data class LocalContactsUserEntity( @PrimaryKey(autoGenerate = false) val mobileNumber: String, var name: String ){ override fun toString(): String { return Gson().toJson(this) } }
该操作会触发反射序列化,属于重开销操作。如果你的代码中存在日志打印实体、列表绑定调用toString等逻辑,2000条数据批量触发该方法会产生大量额外耗时,很容易导致整体流程变慢。
2. 数据处理线程调度不当
Room返回LiveData默认会在后台线程执行查询,但如果你后续对LiveData返回的结果做了数据转换、过滤等操作,且这些操作被放到主线程执行,也会导致主线程阻塞,看起来像是查询耗时高。
对应优化方案
- 移除实体类中不必要的Gson序列化toString重写,仅在明确需要序列化的场景单独调用Gson转换即可。
- 所有数据转换、过滤逻辑都放到后台线程执行,可以配合
Transformations或者kotlin的flow操作符完成。
二、RecyclerView加载大数据集优化方案
经排查确认是RecyclerView加载耗时的话,可按以下优先级做优化:
- 开启固定尺寸优化
如果你的联系人列表Item高度是固定的,初始化RecyclerView时添加配置:
recyclerView.setHasFixedSize(true)
该配置会让RecyclerView避免每次刷新数据时反复测量根布局尺寸,大幅减少布局计算开销。
- 使用DiffUtil做局部刷新
禁止每次拿到全量数据就调用notifyDataSetChanged()做全量刷新,该方法会重绘所有可见Item,性能极差。可以继承ListAdapter,内置DiffUtil能力,只需要实现新旧数据对比逻辑,后续每次提交新列表时会自动计算差异做局部更新,刷新性能可提升3~5倍。 - 优化Item布局性能
- 优先用ConstraintLayout实现扁平化的Item布局,减少布局嵌套带来的递归测量开销
- 移除Item中不必要的背景、阴影等绘制逻辑,减少过度绘制
- 文字类展示内容提前做好格式处理,不要在绑定阶段做字符串拼接、正则匹配等操作
- 可选:分页加载适配
如果后续联系人数据量上涨到万条以上,可以配合Room的PagingSource和Paging3组件做分页加载,每次只加载当前屏幕可见+预加载的几十条数据,内存占用和加载速度都会有明显提升,2000条量级的话非必须,可按需适配。 - 禁止在
onBindViewHolder中执行重操作onBindViewHolder是滑动时频繁调用的方法,不要在该方法内做异步请求、数据库查询、对象创建等重开销操作,所有需要的内容都提前在数据层预处理完成,绑定阶段直接赋值给控件即可。
内容的提问来源于stack exchange,提问作者Gulab Sagevadiya
相关产品推荐
相关产品推荐

