Kotlin Flow:当片段变得不可见时取消订阅 SharedFlow

ade*_*111 5 android kotlin kotlin-coroutines kotlin-flow kotlin-coroutines-flow

我读过类似的主题,但找不到正确的答案:

在我的Repository班级我感冒了Flow,我想分享给 2 Presenters/ViewModels所以我的选择是使用shareIn运算符。

让我们看一下 Android 文档的示例:

val latestNews: Flow<List<ArticleHeadline>> = flow {
    ...
}.shareIn(
    externalScope,  // e.g. CoroutineScope(Dispatchers.IO)?
    replay = 1,
    started = SharingStarted.WhileSubscribed()
)
Run Code Online (Sandbox Code Playgroud)

什么文档建议externalScope参数:

用于共享流的 CoroutineScope。这个范围应该比任何消费者活得更久,以根据需要保持共享流的活动。

但是,寻找有关如何停止订阅 aFlow的答案,第二个链接中投票最多的答案说:

解决方案不是取消流程,而是取消流程的范围。

对我来说,这些答案在SharedFlow的情况下是矛盾的。不幸的是,即使在调用它之后,我的Presenter/ViewModel仍然会收到最新的数据onCleared

如何防止?这是我如何Flow在我的Presenter/ 中使用它的示例ViewModel

fun doSomethingUseful(): Flow<OtherModel> {
    return repository.latestNews.map(OtherModel)
Run Code Online (Sandbox Code Playgroud)

如果这可能有帮助,我正在使用 MVI 架构,以便doSomethingUseful对用户创建的某些意图做出反应。

ade*_*111 5

感谢马克·基恩的评论和帖子,我想我取得了满意的结果。

我知道shareIn参数中定义的范围不必与我的消费者操作的范围相同。BasePresenter将/中的范围更改BaseViewModelCoroutineScopetoviewModelScope似乎解决了主要问题。您甚至不需要手动取消此范围,如Android 文档中所定义:

init {
    viewModelScope.launch {
        // Coroutine that will be canceled when the ViewModel is cleared.
    }
}
Run Code Online (Sandbox Code Playgroud)

请记住,默认viewModelScope调度程序Main并不明显,而且可能不是您想要的!要更改调度程序,请使用viewModelScope.launch(YourDispatcher).

更重要的是,我的热是从回调API(基于回调API)创建的SharedFlow另一个冷转变而来的FlowcallbackFlowChannelsAPI - 这很复杂......)

将集合范围更改为 后viewModelScope,我在从该 API 发出新数据时遇到ChildCancelledException: Child of the scoped flow was cancelled异常。这个问题在 GitHub 上的两个问题中都有详细记录:

如前所述,使用offer和 的发射之间存在细微差别send

Offer 用于非挂起上下文,而 send 用于挂起上下文。

不幸的是,就传播的异常而言,offer 的发送是非对称的(发送中的 CancellationException 通常被忽略,而名义挂起上下文中的offer 中的 CancellationException 则不会)。

我们希望通过 OfferOrClosed 或更改 Offer 语义在 #974 中修复它

至于 1.4.2 的 Kotlin 协程,#974 尚未修复 - 我希望它会在不久的将来避免意外CancellationException

最后,我建议使用运算符started中的参数shareIn。在完成所有这些更改之后,我必须在我的用例中从 更改WhileSubscribed()为。Lazily

如果我发现任何新信息,我会更新这篇文章。希望我的研究能够节省某人的时间。