成功后,Kotlin Flow 在片段中仍处于活动状态

时间:2021-05-26 06:13:52

标签: android kotlin android-fragments kotlin-coroutines kotlin-flow

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

我无法回到上一个片段,这是问题所在: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())
    }
}

当我从下一个片段按回时,由于状态仍然是 Success 正在收集流并转到下一个片段,我已经尝试 this.cancel 取消创建时的协程,但仍然无法正常工作。 我该怎么办?

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

我提出了一个解决方法,即使用

在成功时将流状态重置回 State.EMPTY
viewModel.resetState()

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

视图模型代码:

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

2 个答案:

答案 0 :(得分:1)

如果您的 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()
    }

答案 1 :(得分:-1)

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

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