微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

StateFlow 最后一个值在 ui

如何解决StateFlow 最后一个值在 ui

最近我一直在使用 StateFlow、SharedFlow 和 Channels API,但在尝试将我的代码从 LiveData 迁移到表示层中的 StateFlow 时,我遇到了一个常见用例。

我面临的问题是,当我发出数据并在 viewmodel 中收集它时,我可以将值设置为 mutableStateFlow,当它最终到达片段时,它会使用 Toast 显示一些信息性消息让用户知道是否发生错误或一切正常。接下来,有一个按钮可以导航到另一个片段,但是如果我返回到已经包含失败意图结果的前一个屏幕,它会再次显示 Toast。这正是我想要弄清楚的。如果我已经收集了结果并将消息显示用户,我不想继续这样做。如果我导航到另一个屏幕并返回(当应用程序从后台返回时也会发生这种情况,它会再次收集最后一个值)。 LiveData 没有发生这个问题,我只是做了完全相同的事情,从存储库公开流并通过 viewmodel 中的 LiveData 收集。

代码

class SignInviewmodel @Inject constructor(
    private val doSignIn: SigninUseCase
) : viewmodel(){

    private val _userResult = MutableStateFlow<Result<String>?>(null)
    val userResult: StateFlow<Result<String>?> = _userResult.stateIn(viewmodelScope,SharingStarted.Lazily,null) //Lazily since it's just one shot operation

    fun authenticate(email: String,password: String) {
        viewmodelScope.launch {
            doSignIn(LoginParams(email,password)).collect { result ->
                Timber.e("I just received this $result in viewmodel")
                _userResult.value = result
            }
        }
    }
    
}

然后在我的片段中:

override fun onViewCreated(...){
super.onViewCreated(...)

launchAndRepeatWithViewLifecycle {
            viewmodel.userResult.collect { result ->
                when(result) {
                    is Result.Success -> {
                        Timber.e("user with code:${result.data} logged in")
                        shouldShowLoading(false)
                        findNavController().navigate(SignInFragmentDirections.toHome())
                    }
                    is Result.Loading -> {
                        shouldShowLoading(true)
                    }
                    is Result.Error -> {
                        Timber.e("error: ${result.exception}")
                        if(result.exception is Failure.ApiFailure.BadRequestError){
                            Timber.e(result.exception.message)
                            shortToast("credentials don't match")
                        } else {
                            shortToast(result.exception.toString())
                        }

                        shouldShowLoading(false)
                    }
                }
            }
}

launchAndRepeatWithViewLifecycle 扩展函数

inline fun Fragment.launchAndRepeatWithViewLifecycle(
    minActiveState: Lifecycle.State = Lifecycle.State.STARTED,crossinline block: suspend Coroutinescope.() -> Unit
) {
    viewLifecycleOwner.lifecycleScope.launch {
        viewLifecycleOwner.lifecycle.repeatOnLifecycle(minActiveState) {
            block()
        }
    }
}

关于为什么会发生这种情况以及如何使用 StateFlow 解决它的任何想法?我也尝试过使用 replay = 0 的 SharedFlow 和使用 receiveAsFlow() 的 Channels 但随后出现了其他问题。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。