You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 29+下两个RecyclerView共享PagingDataFlow崩溃问题求解

解决方案:双RecyclerView共享PagingDataFlow并避免崩溃

崩溃原因分析

  1. 冷流重复订阅问题:Paging 3的PagingDataFlow是冷流,每次调用collectLatest都会触发新的分页请求,且每个PagingData实例只能被一个适配器提交一次,重复提交会导致内部状态混乱。
  2. 主线程时机冲突: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(shareIn API的最低要求),Paging 3版本≥3.0.0,兼容Android 29+无压力。

内容的提问来源于stack exchange,提问作者kelalaka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 21:05:59