【问题标题】:Kotlin Flow still active in fragment after success成功后 Kotlin Flow 在片段中仍然活跃
【发布时间】:2021-05-26 06:13:52
【问题描述】:

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

我无法回到上一个片段,这是问题所在: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 中,我认为这不是最好的方法,有什么建议吗?

ViewModel 代码:

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

【问题讨论】:

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


    【解决方案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()
        }
    
    

    【讨论】:

    • 使用.asLiveData() 修复它的livedata,我如何自己管理流的生命周期?
    • 此时您不需要对 Flow 进行任何管理。将Flow的collect中的代码放到LiveData的监听器中,LiveData会自动管理它的生命周期。
    • 只是一个问题,我想完全消除 LiveData 的使用,这是唯一的方法吗?
    • 这与您提供的流程有关。如果是 StatedFlow 这样的 Hot 流,则可能不会使用 LiveData。在这种情况下,您需要自己管理生命周期。
    • 我在我的虚拟机中使用了这样的状态流,用虚拟机代码 private val _emailCheckResponse = MutableStateFlow&lt;State&gt;(State.Empty) val emailCheck: StateFlow&lt;State&gt; get() = _emailCheckResponse 更新了问题,并且使用 States.SUCCESS/FAILURE 进行改造的结果进入这些流程
    【解决方案2】:

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

    向下滚动到底部可以解决我的问题。

    【讨论】:

      猜你喜欢
      • 2023-04-07
      • 1970-01-01
      • 2011-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-07
      相关资源
      最近更新 更多