kotlin 使用repeatOnLifecycle API重新收集流

qojgxg4l  于 2023-01-09  发布在  Kotlin
关注(0)|答案(3)|浏览(909)

当我使用repeatOnLifecycle收集Activity/Fragment中的StateFlow,然后导航到另一个Activity,然后返回到基本Activity时,即使我不更新stateFlow,流也会重新收集。
例如:

    • 在视图模型中**
private var _deletionStatusStateFlow = MutableStateFlow(0)
val deletionStatusStateFlow = _deletionStatusStateFlow.asStateFlow()

然后我在片段中观察到:

viewLifecycleOwner.lifecycleScope.launch {
    viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED){

         deleteAccountViewModel.deletionStatusStateFlow.collect {

           if (it == 1){
             startActivity(AnyActivity)
           }
         }
     }
}
    • 每次单击BackKey时,活动都保持打开状态**

但如果我使用相同示例的LiveData ......观察块将不会再次执行(当返回到片段视图中的STATRED状态时)
如何在StateFlow中实现与LiveData类似的行为?
有一个简单用法的解决方案:我使用flowWithLifecycle(...).distinctUntilChanged()时就是这样,但这很复杂:

val results: StateFlow<SearchResult> =
    queryFlow.mapLatest { query ->
        repository.search(query)
    }.stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000L),
        initialValue = SearchResult.EMPTY
    )

上游将被重新生成(并且这将花费上游repo读取)

64jmpszr

64jmpszr1#

这是预期的行为,因为Activity和Fragments可以被多次重新创建,这就是repeatOnLifecycle存在的原因。
您需要将数据 Package 在一个类中,该类还具有一个布尔属性,指示关联的导航事件是否已发生。Activity/Fragment中的收集器在执行导航事件时,还应调用ViewModel函数,ViewModel使用该函数更新Flow以发出一个事件,在该事件中导航事件被视为已处理。这对于stateIn来说太复杂了。我希望使用MutableStateFlow,这样您就可以根据来自Activity的反馈更新值。
此过程在此处的Android文档中进行了描述。

a2mppw5e

a2mppw5e2#

导航通常最好用常规Flow表示(或SharedFlow),而不是StateFlow。导航事件只应被收集器使用一次,然后丢弃,这与可以随意重新读取的UI状态相反。如果不丢弃该事件(一个Flow为你做这件事)你会得到你所看到的行为,其中导航被触发多次。
考虑到这一点,我将从视图模型中将导航公开为Flow,与您可能拥有的任何UI状态流分开。

    • 查看模型**
// Define our UI state flow
private val _stateFlow = MutableStateFlow(initialState) // Side note: this mutable flow does not need to use "var", we can and should use "val"
val stateFlow: StateFlow<UiState> = _stateFlow.asStateFlow()

// Define our navigation event flow
val navigationFlow: Flow<NavigationEvent> = ... // Logic to construct navigation flow goes here.
// You might use flow { ... } or a MutableSharedFlow<NavigationEvent> depending on your needs, but you only need to expose a Flow.

如果这个视图模型有不止一种类型的导航事件,那么您将希望清楚地定义它们,如下所示。

    • 型号**
sealed interface NavigationEvent {
    object GoToXyzActivity : NavigationEvent
}

现在,我们将收集从Activity的视图模型中公开的流。

    • 活动**
// Collect our UI state
viewLifecycleOwner.lifecycleScope.launch {
    deleteAccountViewModel.stateFlow.collect {    
      
  viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
            // Update the view based on the UI state here
        }
    }
}

// Collect our navigation events
viewLifecycleOwner.lifecycleScope.launch {
    // If your navigation doesn't interact with the activity's view,
    // we actually don't need to use `repeatOnLifecycle()`, since `startActivity()`
    // does not required the view to exist to work - we could be in
    // any stage of the activity's `Lifecycle` and that's fine.
    deleteAccountViewModel.navigationFlow.collect { navEvent ->
        when (navEvent) {
            GoToXyzActivity -> startActivity(XyzActivity)
            // Other navigation can go here
        }
    }
}

仅供参考,这是来自状态流的Kotlin文档
与使用流构建器构建的冷流不同,StateFlow是热流:从流中收集不会触发任何生产者代码。
这意味着,只要您将结果存储在视图模型某处的StateFlow中,就不会重新触发对repo的上游调用。

busg9geu

busg9geu3#

尝试将生命周期从已启动更改为已创建

viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED)

到这个代码

viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.CREATED)

前者告诉repeatOnLifecycle在生命周期至少开始时重复collect,这意味着每次生命周期处于开始状态时,它都将重新收集所述流
repeatOnLifecycle(Lifecycle.State.STARTED)不同,后者仅在生命周期处于CRATED状态时重新收集流,这仅发生在创建片段CMIMW时

相关问题