Kotlin Flow 成功后仍然活跃在 fragment 中

Kotlin Flow still active in fragment after success

我有一个片段根据结果发出网络请求,我正在导航到下一个片段。

我无法返回到之前的片段,这是问题所在:https://streamable.com/4m2vzg

这是上一个片段中的代码

class EmailInputFragment :
    BaseFragment<FragmentEmailInputBinding>(FragmentEmailInputBinding::inflate) {

    private val viewModel by viewModels<EmailInputViewModel>()
    private lateinit var progressButton: ProgressButton

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        binding.emailToolbar.setNavigationOnClickListener {
            val activity = activity as AuthActivity
            activity.onSupportNavigateUp()
        }
        binding.emailNextButton.pbTextview.text = getString(R.string.next)
        binding.emailNextButton.root.setOnClickListener {
            checkValidEmail()
        }
        binding.enterEmail.setOnEditorActionListener { _, actionId, _ ->
            if (actionId == EditorInfo.IME_ACTION_DONE) {
                checkValidEmail()
            }
            false
        }
        binding.enterEmail.doAfterTextChanged {
            binding.enterEmailLayout.isErrorEnabled = false
        }
        viewLifecycleOwner.lifecycleScope.launch {
            viewModel.emailCheck.collect {
                when (it) {
                    State.Empty -> {
                    }
                    is State.Failed -> {
                        Timber.e(it.message)
                        progressButton.buttonFinished("Next")
                    }
                    State.Loading -> {
                        progressButton.buttonActivate("Loading")
                    }
                    is State.Success<*> -> {
                        it.data as EmailCheckModel
                        when (it.data.registered) {
                            // New User
                            0 -> {
                                findNavController().navigate(
                                    EmailInputFragmentDirections.actionEmailFragmentToSignupFragment(
                                        binding.enterEmail.text.toString().trim()
                                    )
                                )
                            }
                            // Existing User
                            1 -> {
                                findNavController().navigate(
                                    EmailInputFragmentDirections.actionEmailFragmentToPasswordInputFragment(
                                        binding.enterEmail.text.toString().trim()
                                    )
                                )
                            }
                            // Unverified user
                            2 -> {
                                findNavController().navigate(
                                    EmailInputFragmentDirections.actionEmailFragmentToVerifyUserFragment(
                                        "OK"
                                    )
                                )
                            }
                        }
                    }
                }
            }
        }
    }

    private fun checkValidEmail() {
        if (!binding.enterEmail.text.toString().trim().isValidEmail()) {
            binding.enterEmailLayout.error = "Please enter valid Email ID"
            return
        }
        progressButton = ProgressButton(requireContext(), binding.emailNextButton.root)
        viewModel.checkUser(binding.enterEmail.text.toString().trim())
    }
}

当我从下一个片段按回时,由于状态仍然是成功,正在收集流并转到下一个片段,我已经尝试 this.cancel 在创建时取消协程但仍然没有'不工作。 我该怎么做?

将流收集移动到按钮的 onClick 会引发无法找到目的地的导航操作的错误

我使用 解决方法 成功使用

将流程状态重置回 State.EMPTY
viewModel.resetState()

在 onSuccess 中,我认为这不是最好的方法,有什么建议吗?

视图模型代码:

private val _emailCheckResponse = MutableStateFlow<State>(State.Empty)
val emailCheck: StateFlow<State> get() = _emailCheckResponse 

如果你的viewModel.emailCheck流是热流,那么你需要自己管理它的生命周期。如果不是热点Flow,那么就需要用LiveData来控制接口,而不是简单的采集Flow。需要将流转为LiveData,并在相应位置添加Observer到LiveData。

Cold Flow中没有API接口生命周期相关,但LiveData中已经管理了生命周期。


viewModel.emailCheckLiveData.observe(viewLifecycleOwner, {
             when (it) {
                    State.Empty -> {
                    }
                    is State.Failed -> {
                        Timber.e(it.message)
                        progressButton.buttonFinished("Next")
                    }
                    State.Loading -> {
                        progressButton.buttonActivate("Loading")
                    }
                    is State.Success<*> -> {
                        it.data as EmailCheckModel
                        if (it.data.registered) {
                            val action =
                                EmailInputFragmentDirections.actionEmailFragmentToPasswordInputFragment(
                                    binding.enterEmail.text.toString().trim()
                                )
                            findNavController().navigate(action)
                        } else {
                            val action =
                                EmailInputFragmentDirections.actionEmailFragmentToSignupFragment(
                                    binding.enterEmail.text.toString().trim()
                                )
                            findNavController().navigate(action)
                        }
                    }
        })

您需要定义emailCheckLiveData。在 Flow.asLiveData()


    private val _emailCheckResponse = MutableStateFlow<State>(State.Empty)
    val emailCheck: StateFlow<State> get() = _emailCheckResponse

    private var mJob: Job? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        lifecycleScope.launchWhenResumed {
            if (mJob?.isActive == true) return
            mJob = _emailCheckResponse.collectLatest {
                when (it) {
                    State.Empty -> {
                    }
                    is State.Failed -> {
                        Timber.e(it.message)
                        progressButton.buttonFinished("Next")
                    }
                    State.Loading -> {
                        progressButton.buttonActivate("Loading")
                    }
                    is State.Success<*> -> {
                        it.data as EmailCheckModel
                        if (it.data.registered) {
                            val action =
                                EmailInputFragmentDirections.actionEmailFragmentToPasswordInputFragment(
                                    binding.enterEmail.text.toString().trim()
                                )
                            findNavController().navigate(action)
                        } else {
                            val action =
                                EmailInputFragmentDirections.actionEmailFragmentToSignupFragment(
                                    binding.enterEmail.text.toString().trim()
                                )
                            findNavController().navigate(action)
                        }
                    }
                }
            }
        }
    }

    override fun onDestroy() {
        mJob?.apply {
            if (isActive) cancel()
        }
        super.onDestroy()
    }

过了一段时间,偶然发现了这篇文章。 https://proandroiddev.com/flow-livedata-what-are-they-best-use-case-lets-build-a-login-system-39315510666d

向下滚动到底部找到了我的问题的解决方案。