【问题标题】:Retrieving ViewModel variable after a coroutine has updated the variable and the coroutine is completed, Android Kotlin Coroutines在协程更新变量并且协程完成后检索 ViewModel 变量,Android Kotlin Coroutines
【发布时间】:2021-03-29 17:11:23
【问题描述】:

我在Kotlin中开发一个android的应用,编写重置密码片段,逻辑如下:

用户输入电子邮件地址以重置密码。 输入电子邮件并按下“发送重置代码”后,ViewModel 功能 触发forgetPasswordViewModel.sendAuthCode(),通过repository向Room数据库发送查询,检查房间数据库中是否存在包括电子邮件地址在内的用户数据,如果该电子邮件地址存在且结果为not null,则系统检索重置代码密码(authCode)并将其通过电子邮件发送给用户。如果系统中不存在电子邮件地址(由 null userData 指示),则视图模型中的布尔变量 validEmail 设置为 true。这有助于我在 ForgetPassword 片段中生成适当的 toast 消息。

我在 ForgetPassword 片段中调用 forgetPasswordViewModel.sendAuthCode() 方法,然后检查 validEmail 变量,该变量将指示系统中是否已存在电子邮件,但变量 validEmail 总是返回 false,因为协程 @987654328 @ 仍在后台线程中运行,并且直到那时才更新布尔变量 validEmailyet,因此在运行片段的主线程上,代码继续运行以下语句,从 ViewModel 检索 validEmail此时仍然为 false,因为协程函数 forgetPasswordViewModel.sendAuthCode() 尚未将变量设置为 true。

我可以使用 Kotlin 中的任何函数,以便我的代码将等到协程完成执行并在我的代码继续执行之前更新变量。我在 StackOverFlow 上看到了一些解决方案,人们建议使用 join() 但我只有一个协程,布尔变量 validEmail 是 ViewModel 范围变量。任何人都可以指导我正确的方向。这是我的代码:

查看模型:

class ForgetPasswordViewModel(val repository: Repository) : ViewModel() {

    //Variable to contain user email address
    lateinit var emailAddress: String
    var validEmail: Boolean = false


    //Getting instance of Repository
    val repo = Repository()

    //Send Password reset code to email

    fun sendAuthCode() {
        viewModelScope.launch {
            val userData = repo.getUserDataByEmail(emailAddress)
            if (userData != null) {
                val authCode = userData.authCode
                //Send email to new user with account details
                val sender = SendMail(
                    AppContext.appContext!!,
                    emailAddress,
                    "Password reset code",
                    "Dear ${userData.firstName},\n\n" +
                            " This is your reset code.\n\n +
                            "Password reset code: $authCode \n"
                )
                sender.execute()
                validEmail = true //here I set it to true to indicate that things went well
            }
        }
    }
}

这是 ForgetPassword 片段中的 sn-p:

 // Set onClickListener to btnRequestPassword button
    viewBinding.btnRequestPassword.setOnClickListener{
        forgetPasswordViewModel.emailAddress = emailAddress.text.toString()
        if(forgetPasswordViewModel.emailAddress.length < 3) { //The theoretical minimum length of any email address is 3
            ValidationChecks.showToast("Enter valid email address",context)
        } else{
            forgetPasswordViewModel.sendAuthCode()
            if(!forgetPasswordViewModel.validEmail){
                Toast.makeText(context, "Email not found in the system",Toast.LENGTH_SHORT)
            } else {
                Toast.makeText(context, "Password reset code sent successfully!",Toast.LENGTH_SHORT)
            }

如果有人能将我推向正确的方向,我将不胜感激。 亲切的问候, 萨利克

【问题讨论】:

    标签: android kotlin model-view-controller kotlin-coroutines


    【解决方案1】:

    由于您期望同步行为,您可以在片段中启动协程并暂停 sendAuthCode 函数。

    片段:

    ...
    viewLifecycleOwner.lifecycleScope.launch {
        val validEmail = forgetPasswordViewModel.sendAuthCode()
        if(!validEmail){
            Toast.makeText(context, "Email not found in the system",Toast.LENGTH_SHORT)
        } else {
            Toast.makeText(context, "Password reset code sent successfully!",Toast.LENGTH_SHORT)
        }
    }
    ...
    

    视图模型:

    suspend fun sendAuthCode(): Boolean {
        val userData = repo.getUserDataByEmail(emailAddress)
        if (userData != null) {
            val authCode = userData.authCode
            //Send email to new user with account details
            val sender = SendMail(
                AppContext.appContext!!,
                emailAddress,
                "Password reset code",
                "Dear ${userData.firstName},\n\n" +
                        " This is your reset code.\n\n +
                "Password reset code: $authCode \n"
            )
            sender.execute()
            return true
        }
    
        return false
    }
    

    确保您的 DAO 方法处于挂起状态,以便它们在主线程之外运行。或者如果在操作中间有阻塞调用,则更改协程上下文,因为viewLifecycleOwner.lifecycleScope.launch {...} 在主线程上运行。这样的事情会做:

    suspend fun sendAuthCode(): Boolean = withContext(Dispatchers.IO) {
        ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-18
      • 2018-03-22
      • 1970-01-01
      • 2023-02-14
      • 2018-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多