Android 29+下两个RecyclerView共享PagingDataFlow崩溃问题求解
解决方案:双RecyclerView共享PagingDataFlow并避免崩溃
崩溃原因分析
- 冷流重复订阅问题:Paging 3的
PagingDataFlow是冷流,每次调用collectLatest都会触发新的分页请求,且每个PagingData实例只能被一个适配器提交一次,重复提交会导致内部状态混乱。 - 主线程时机冲突:
post将提交操作延迟到消息队列执行,可能恰好撞上RecyclerView的布局计算或滚动阶段,触发系统抛出的状态检查异常。
针对Android 29+的修复方案
1. 将冷流转为共享热流
把ViewModel返回的冷流转换为共享热流,确保多个订阅者复用同一数据源,避免重复请求:
方式1:在ViewModel层处理
// ViewModel中修改分页流返回逻辑 fun getBusinessvRecords(request: RecordSearchRequest): Flow<PagingData<YourDataModel>> { return Pager( config = PagingConfig(pageSize = 20), pagingSourceFactory = { YourPagingSource(request) } ).flow .shareIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), // 订阅者消失后保留5秒连接 replay = 1 // 缓存最新1页数据,新订阅者可立即获取 ) }
方式2:在View层(Fragment/Activity)处理
如果无法修改ViewModel,可在View层转换:
lifecycleScope.launch { val sharedPagingFlow = viewModel.getBusinessvRecords(recordSearchRequest) .shareIn( lifecycleScope, started = SharingStarted.WhileSubscribed(5000), replay = 1 ) // 后续订阅这个共享流 }
2. 安全提交数据到适配器
去掉post的盲目延迟,改为先检查RecyclerView状态,再选择合适时机提交:
lifecycleScope.launch { val sharedPagingFlow = viewModel.getBusinessvRecords(recordSearchRequest) .shareIn( lifecycleScope, started = SharingStarted.WhileSubscribed(5000), replay = 1 ) // 左侧RecyclerView订阅 launch { sharedPagingFlow.collectLatest { pagingData -> submitSafely( adapter = pagingAdapterLeft, recyclerView = binding.recyclerViewLeft, lifecycle = viewLifecycleOwner.lifecycle, pagingData = pagingData ) } } // 右侧RecyclerView订阅 launch { sharedPagingFlow.collectLatest { pagingData -> submitSafely( adapter = pagingAdapterRight, recyclerView = binding.recyclerViewRight, lifecycle = viewLifecycleOwner.lifecycle, pagingData = pagingData ) } } } // 封装安全提交的扩展函数 private fun <T> submitSafely( adapter: PagingDataAdapter<T, *>, recyclerView: RecyclerView, lifecycle: Lifecycle, pagingData: PagingData<T> ) { if (!recyclerView.isComputingLayout) { adapter.submitData(lifecycle, pagingData) } else { // 若当前正在布局,延迟到主线程空闲时执行 recyclerView.post { adapter.submitData(lifecycle, pagingData) } } }
3. 额外注意事项
- 生命周期绑定:使用
viewLifecycleOwner.lifecycle(Fragment场景)而非Activity的lifecycle,确保页面销毁时自动取消订阅,避免内存泄漏。 - 分页状态同步:如果需要两个RecyclerView的滚动位置同步,可监听其中一个的滚动事件,同步更新另一个的
scrollToPosition。 - 依赖版本:确保Coroutines版本≥1.5.0(
shareInAPI的最低要求),Paging 3版本≥3.0.0,兼容Android 29+无压力。
内容的提问来源于stack exchange,提问作者kelalaka
相关产品推荐
相关产品推荐

