android - Kotlin 流 : unsubscribe from SharedFlow when Fragment becomes invisible

标签 android kotlin kotlin-flow kotlin-coroutines

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

  • How to end / close a MutableSharedFlow?
  • Kotlin Flow: How to unsubscribe/stop
  • StateFlow and SharedFlow. Making cold flows hot using shareIn - Android 文档
  • Introduce SharedFlow - GH 讨论由 Roman Elizarov
  • 发起

    在我的 Repository上课我感冒了Flow我想分享给 2 Presenters/ViewModels所以我的选择是使用shareIn运算符(operator)。
    让我们看一下 Android 文档的示例:
    val latestNews: Flow<List<ArticleHeadline>> = flow {
        ...
    }.shareIn(
        externalScope,  // e.g. CoroutineScope(Dispatchers.IO)?
        replay = 1,
        started = SharingStarted.WhileSubscribed()
    )
    
    文档对 externalScope 的建议是什么?范围:

    A CoroutineScope that is used to share the flow. This scope should live longer than any consumer to keep the shared flow alive as long as needed.


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

    A solution is not to cancel the flow, but the scope it's launched in.


    对我来说,这些答案在 SharedFlow 中是矛盾的。的情况。不幸的是我的Presenter/ViewModel即使在其 onCleared 之后仍会收到最新数据被称为。
    如何防止这种情况?这是我如何使用 Flow 的示例在我的Presenter/ViewModel :
    fun doSomethingUseful(): Flow<OtherModel> {
        return repository.latestNews.map(OtherModel)
    
    如果这可能有帮助,我正在使用 MVI 架构,所以 doSomethingUseful对用户创建的一些 Intent 使用react。

    最佳答案

    感谢 Mark Keen 的评论和帖子,我想我设法获得了令人满意的结果。
    我了解 shareIn 中定义的范围参数不必与我的消费者操作的范围相同。在 BasePresenter 中更改范围/BaseViewModel来自 CoroutineScopeviewModelScope似乎解决了主要问题。您甚至不需要手动取消此范围,如 Android docs 中所定义。 :

    init {
        viewModelScope.launch {
            // Coroutine that will be canceled when the ViewModel is cleared.
        }
    }
    
    请记住默认 viewModelScope调度员是 Main这并不明显,它可能不是你想要的!要更改调度程序,请使用 viewModelScope.launch(YourDispatcher) .
    更何况我的热门SharedFlow由另一种寒冷转变而来Flow创建于 callbackFlow回调 API(基于 Channels API - 这很复杂...)
    将收集范围更改为 viewModelScope 后,我得到 ChildCancelledException: Child of the scoped flow was cancelled从该 API 发出新数据时出现异常。这个问题在 GitHub 上的两个问题中都有很好的记录:
  • kotlinx.coroutines.flow.internal.ChildCancelledException: Child of the scoped flow was cancelled
  • SendChannel.offer should never throw

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

    offer is for non-suspending context, while send is for suspending ones.


    offer is, unfortunately, non-symmetric to send in terms of propagated exceptions (CancellationException from send is usually ignored, while CancellationException from offer in nom-suspending context is not).


    We hope to fix it in #974 either with offerOrClosed or changing offer semantics


    至于 1.4.2 的 Kotlin Coroutines,#974 还没有修复 - 我希望它会在不久的将来避免意外 CancellationException .
    最后,我建议玩started shareIn 中的参数运算符(operator)。在所有这些更改之后,我不得不从 WhileSubscribed() 更改至Lazily在我的用例中。
    如果我发现任何新信息,我会更新这篇文章。希望我的研究能节省一些人的时间。

    关于android - Kotlin 流 : unsubscribe from SharedFlow when Fragment becomes invisible,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66267722/

    相关文章:

    Android 模拟器与真实设备 [2013]

    .indexOf() 处的 Android 空指针异常

    java - 登录后添加 header 拦截器 - Retrofit

    java - @ProjectedPayload/ProjectingJackson2HttpMessageConverter 的默认值

    kotlin - 为什么 Kotlin 编译器不强制我初始化接口(interface)中定义的 val?

    android - 如何在 Kotlin 中合并流和 channel ?

    java - 如何从此字符串中返回一些随机数?

    java - 将 ImageViewTouch 的缩放类型设置为 CenterCrop 后,它无法缩放

    android - 我如何使用 Android 存储库中的流程?

    android - 从消费者发送消息到回调流