notifyDataSetChanged()在onStart或onPause中调用未触发RecyclerView刷新问题
问题根因
- Activity退到后台时,系统会缓存当前的UI快照,返回Activity时系统会先展示这个快照实现快速响应,这就是你一开始看到1台旧设备的原因。
- 你在
onPause里调用的notifyDataSetChanged不会触发实际绘制,后台Activity的UI更新会被系统暂停,等到回到前台才会执行,完全达不到提前清列表的效果。 - LiveData默认只在生命周期处于
STARTED/RESUMED状态时才会发送事件,等系统展示完旧快照、Activity进入STARTED状态后,LiveData才会推送最新的2台设备的数据,触发UI更新,就出现了闪烁效果。
可行解决方案
方案1:预更新列表数据,避免快照后才刷新
在onStart生命周期里,在系统绘制UI之前,先主动获取ViewModel里最新的设备列表,提前更新到适配器数据源里,这样系统第一次绘制用的就是最新数据,不会先展示旧快照:
override fun onStart() { super.onStart() // 主动拉取最新数据提前更新数据源 val latestDeviceList = viewModel.deviceList.value ?: emptyList() deviceItems.clear() deviceItems.addAll(latestDeviceList.map { deviceItemMapper.apply(it) }) adapter.notifyDataSetChanged() }
记得删掉你之前加的onPause里的清列表逻辑,完全无用还会多触发一次无效刷新
方案2:替换全量刷新为DiffUtil,彻底消除闪烁
你现在用的notifyDataSetChanged是全量刷新RecyclerView,会触发所有item重绘,本身就容易出现闪烁。用DiffUtil可以只更新变化的item,不会有闪屏问题:
- 先定义DiffCallback:
class DeviceDiffCallback( private val oldList: List<Item>, private val newList: List<Item> ) : DiffUtil.Callback() { override fun getOldListSize() = oldList.size override fun getNewListSize() = newList.size override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { // 替换为你设备item的唯一标识,比如设备id return oldList[oldItemPosition].deviceId == newList[newItemPosition].deviceId } override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { // 比较两个item内容是否完全一致 return oldList[oldItemPosition] == newList[newItemPosition] } }
- 修改LiveData观察者逻辑:
private fun subscribeToChanges() { viewModel.deviceList.observe(viewLifecycleOwner, Observer { deviceList -> val newItems = deviceList.map { deviceItemMapper.apply(it) } val diffResult = DiffUtil.calculateDiff(DeviceDiffCallback(deviceItems, newItems)) deviceItems.clear() deviceItems.addAll(newItems) diffResult.dispatchUpdatesTo(adapter) }) }
两种方案可以搭配使用,优化效果更好。
内容的提问来源于stack exchange,提问作者user3826764
相关产品推荐
相关产品推荐

