【问题标题】:LiveData onChanged called multiple times with the same data when using FirebaseAuth使用 FirebaseAuth 时使用相同数据多次调用 LiveData onChanged
【发布时间】:2020-05-07 07:13:13
【问题描述】:

我有一个使用电子邮件和密码实现 Firebase 身份验证的登录活动。它所做的是将用户输入的电子邮件和密码发送到 FirebaseAuth 函数并通过 LiveData 检索响应,然后观察它。

问题是,当登录失败或凭证无效时,它应该toast一条消息,但实际上发生的是它toast多次,可能超过4次(它使toast显示很长时间)。

登录活动

    private fun signInWithEmailAndPassword(email: String, password: String){
        val authResult = viewModel.authWithEmailPassword(email, password)
        handleAuthenticationResult(authResult)
    }

    private fun handleAuthenticationResult(authResult: LiveData<Resource<AuthResult>>){
        authResult.observe(this, object : Observer<Resource<AuthResult>>{
            override fun onChanged(it: Resource<AuthResult>?) {
                when(it){
                    is Resource.Loading -> {
                        toggleLoadingIndicator(true)
                    }
                    is Resource.Success -> {
                        toggleLoadingIndicator(false)

                        val isNewUser = it.data.additionalUserInfo?.isNewUser ?: true

                        if(isNewUser) gotoIntroActivity()
                        else gotoMainActivity()
                    }
                    is Resource.Failure -> {
                        toggleLoadingIndicator(false)

                        when(it.throwable){
                            is FirebaseAuthException -> Toast.makeText(this@LoginActivity, getString(R.string.error_credentials_invalid), Toast.LENGTH_SHORT).show()
                            else -> Toast.makeText(this@LoginActivity, getString(R.string.error_login_failed), Toast.LENGTH_SHORT).show()
                        }
                    }
                }
            }
        })
    }

视图模型

    fun authWithEmailPassword(email: String, password: String): LiveData<Resource<AuthResult>>{
        return AuthRepository.signInWithEmailAndPassword(email, password)
    }

FirebaseAuth

    private var authResult: MutableLiveData<Resource<AuthResult>> = MutableLiveData()

    fun signInWithEmailAndPassword(email: String, password: String): LiveData<Resource<AuthResult>>{
        authResult.value = Resource.Loading()

        mAuth.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener{
                    if(it.isSuccessful) authResult.value = Resource.Success(it.result!!)
                    else authResult.value = Resource.Failure(it.exception!!)
                }

        return authResult
    }

资源

sealed class Resource<out T> {
    class Loading<out T> : Resource<T>()
    data class Success<out T>(val data: T) : Resource<T>()
    data class Failure<out T>(val throwable: Throwable) : Resource<T>()
}

为什么会发生这种情况以及如何解决?每次Resource.Failure 触发时我都应该删除观察者吗?

【问题讨论】:

    标签: android firebase firebase-authentication android-livedata


    【解决方案1】:

    LiveData 是您只创建和订阅一次的东西。在您的情况下,每次获得身份验证结果时都会订阅它,因此会出现多个重复事件。

    要解决此问题,请执行以下操作:

    1. 在您的视图模型中,声明实时数据:

    val authResult: MutableLiveData&lt;Resource&lt;AuthResult&gt;&gt; = MutableLiveData()

    1. 在 Activity 的 onCreate() 中,订阅此 liveData:

    viewModel.authResult.observe(this, ... [the rest of your code]

    1. 当您得到验证结果时,只需将其发布到 liveData。不要重新订阅实时数据,不要创建新的实时数据。

    authResult.postValue(resource)

    【讨论】:

    • 如果我在 viewmodel 中声明 authresult livedata,如何将值发布到其中?我应该在我的视图模型中观察来自 FirebaseAuth 类的实时数据吗?还是应该改为在 FirebaseAuth 类中声明 livedata?
    • 您可以将 livedata 作为参数传递给 AuthRepository.signInWithEmailAndPassword
    • 对不起,我只是在想一些事情,不将 livedata 作为参数传递给存储库会泄漏视图模型吗?如果存储库仍在获取数据并且用户关闭了活动,会发生什么?
    • 从技术上讲,是的,您正在泄漏视图模型。但是当活动订阅一个 livedata 时,它传递的第一个参数是它的生命周期。因此,当您关闭活动时,它会自动取消订阅实时数据。
    • 您的回答也帮助我解决了在 livedata 中多次执行的问题。谢谢的身体
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-02
    • 1970-01-01
    • 2019-01-18
    • 2021-08-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多