![](/img/trans.png)
[英]Transforming a flow of PagingData from paging3 into a StateFlow of PagingData
[英]Can not collect any flow after emitting Paging3 flow
我正在使用paging3
,并且有两个不同的分页源。 问题是Coroutine Scope
只发出第一个寻呼流
在ViewModel
我有两个分页流程
val pagingFlow1 = Pager(PagingConfig(pageSize = 50, prefetchDistance = 1)) {
pagingSource
}.flow.cachedIn(viewModelScope)
val pagingFlow2 = Pager(PagingConfig(pageSize = 50, prefetchDistance = 1)) {
pagingSource2
}.flow.cachedIn(viewModelScope)
在活动中收集它们
lifecycleScope.launch(Dispatchers.IO) {
viewModel.pagingFlow1.collectLatest { pagingData ->
pagingAdapter.submitData(pagingData)
}
viewModel.pagingFlow2.collectLatest { pagingData ->
pagingAdapter2.submitData(pagingData)
}
}
但是lifecycleScope
只发出pagingFlow1
换句话说分页只在第一个 recyclerView 工作。
当我这次更改顺序时仅适用于pagingFlow2
lifecycleScope.launch(Dispatchers.IO) {
viewModel.pagingFlow2.collectLatest { pagingData ->
pagingAdapter.submitData(pagingData)
}
viewModel.pagingFlow1.collectLatest { pagingData ->
pagingAdapter2.submitData(pagingData)
}
}
为了确保我用基本流程测试它并正常工作
// Flows in ViewModel
val testFlow1 = flowOf(1,2,3)
val testFlow2 = flowOf(4,5,6)
// Activity
lifecycleScope.launch(Dispatchers.IO) {
viewModel.testFlow1.collectLatest { item ->
Log.d(item)
}
viewModel.testFlow2.collectLatest { item ->
Log.d(item)
}
}
我不明白为什么在使用分页时只发出第一个流? 有人给我线索吗?
在尝试不同的事情时,我发现了一些有趣的行为。 如果先收集 pagingFlow,我们将无法收集任何东西
val flow3 = flowOf(1,2,3)
lifecycleScope.launch(Dispatchers.IO) {
flow3.collectLatest { pagingData ->
LogUtils.d("PagingFlow3 $pagingData")
}
viewModel.pagingFlow1.collectLatest { pagingData ->
LogUtils.d("PagingFlow1 $pagingData")
pagingAdapter.submitData(pagingData)
}
viewModel.pagingFlow2.collectLatest { pagingData ->
LogUtils.d("PagingFlow2 $pagingData")
pagingAdapter2.submitData(pagingData)
}
}
第一个flow3
收集比pagingFlow1
收集但pagingFlow2
未收集
如果我们将 flow3 放在pagingFlow1
下面,它就不会被收集
val flow3 = flowOf(1,2,3)
lifecycleScope.launch(Dispatchers.IO) {
viewModel.pagingFlow1.collectLatest { pagingData ->
LogUtils.d("PagingFlow1 $pagingData")
pagingAdapter.submitData(pagingData)
}
flow3.collectLatest { pagingData ->
LogUtils.d("PagingFlow3 $pagingData")
}
viewModel.pagingFlow2.collectLatest { pagingData ->
LogUtils.d("PagingFlow2 $pagingData")
pagingAdapter2.submitData(pagingData)
}
}
仅收集pagingFlow1
collectLatest 暂停直到流程完成,因此您需要启动单独的作业。
此外,您不需要在 IO 调度员上进行调度。
编辑:自发布此答案以来对 Paging 进行了一些更改 - 您调用.submitData
的 Dispatcher 不再重要。 它唯一影响的可能是 init 的分配发生的位置,并且您可能想从非 ui 线程启动这些分配,但通常它不会对性能产生影响。
例如,
lifecycleScope.launch {
viewModel.pagingFlow1.collectLatest { pagingData ->
pagingAdapter.submitData(pagingData) } }
lifecycleScope.launch {
viewModel.pagingFlow2.collectLatest { pagingData ->
pagingAdapter2.submitData(pagingData) } }
首先,您必须意识到,无论何时在代码中使用流,您都在异步检索值。 这意味着对您的活动的 the.collect 调用必须等到从流中发出一个值。 我假设这只会在您从您的案例中读取数据库时发生,如果您在 DAO 返回类型中使用流,则只有在某些 function 更改数据库字段时才会触发。 这意味着,在有人更改数据库之前,不要 go 在代码中传递这一点(换句话说,它会阻塞线程)。 对此的修复是同时运行 your.collect 并行而不是顺序运行。 这在 dlam 的回答中得到了展示,他/她并行运行它们并且它们可以工作。
好的,他/她的回答有效,那我们为什么需要这篇文章呢? 嗯,虽然它适用于用户,但所有这些操作都在主线程上运行,这基本上会冻结 UI 一定数量的帧,因为负责更新 UI 的线程 (Dispatchers.Main) 正忙于执行您的操作。 这可以在您的 logcat 上看到以下消息:
我/编舞:跳过了 xAmountOf 帧。 应用程序可能在其主线程上做了太多工作。
为避免冻结 UI,主线程应仅用于更新 UI。 解决方法是使用 Dispatchers.IO 在后台线程上运行这些操作
java.lang.IllegalArgumentException:已注册具有给定密钥的 SavedStateProvider
这是由于您在流程上调用 .cachedIn(ViewmodelScope) 造成的。 此方法缓存在您的 scope 上的分页数据中,因此您可以在下次需要时更快地访问它,并且当您传入的 scope 终止时,它会被垃圾收集,该错误基本上表示您试图保存第二组数据与已存储的集合相同的密钥。
我假设这些 SavedStateProvider 键可能是自动生成的。 这意味着 whichever.collect 首先被触发(记住它们现在并行运行,所以它可以是任何一个)将使用自动生成的密钥保存,为了简单起见,可以说“1”很好,但是当 second.collect 得到触发并保存其 SavedStateProvider 由于某种原因它还会自动生成“1”作为导致冲突的键。 找到解决此问题的关键是找出导致它们自动生成相同密钥的原因。
我认为他们生成相同密钥的原因是因为它们在并行线程上运行,第二个被触发的密钥在第一个完成保存之前生成密钥,因此第二个不知道该密钥已经存在被缓存。 在 dlam 的回答中没有出现这个错误这一事实支持了这一点,为什么不呢? 最有可能与运行它们的主调度程序有关,而不是 IO 调度程序。
在这一点上我会说修复是为了确保密钥只生成一次,但由于我们无权访问代码,我们在这方面不能做太多......我会说你最好的选择是从您的流程中删除 the.cachedIn 运算符,然后大概这会起作用。
lifecycleScope.launch(Dispatchers.IO) { viewModel.pagingFlow1.collectLatest { pagingData -> LogUtils.d("PagingFlow1 $pagingData") pagingAdapter.submitData(pagingData) } } lifecycleScope.launch(Dispatchers.IO) { viewModel.pagingFlow2.collectLatest { pagingData -> LogUtils.d("PagingFlow2 $pagingData") pagingAdapter2.submitData(pagingData) } }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.