如何return从Coroutine到ViewModel的异步操作

How to return asynchronous operation from Coroutine to ViewModel

我正在尝试理解这最后一步。

我需要一次原子操作,我需要转到 Firebase 并放置我的用户设备令牌。

为此,我只是调用了我的 viewModel 中的一个方法,这将触发我的回购协议,但现在,我不想在我的回购协议中使用 LiveData,相反,我只想 return 来自的资源在那里,但由于是异步方法,我不能只是 return.

我只想在我的 viewModel 中使用 LiveData 而不是在我的存储库中,存储库应该只将对象传送到我的 viewModel 而我的 viewModel 应该将这些传送到我的视图。

查看

viewModel.userToken.observe(this, Observer {
            when (it.status) {
                Status.SUCCESS -> {
                    val user = FirebaseAuth.getInstance().currentUser
                    startActivity(Intent(this, SecondActivity::class.java))
                    Toast.makeText(this, "Welcome ${user!!.uid} !", Toast.LENGTH_SHORT).show()
                    finish()
                }

                Status.ERROR -> {
                }

                else -> {
                    Toast.makeText(this, "error ${it.message}", Toast.LENGTH_SHORT).show()
                }
            }
        })

视图模型

class LoginViewModel: ViewModel() {

    private val useCase = PostUserToken(UserRepo())
    var userToken = liveData(Dispatchers.IO){
        emit(useCase.postUserToken())
    }

}

直到这里,它工作正常,现在,从我的 repo 中的 postUserToken() 方法,我需要 return 一个 Resource<Boolean> 对象到我的视图模型,我如何使用协程来做到这一点?

用例

class PostUserToken(private val repo: UserRepo) {

    suspend fun postUserToken(): Resource<Boolean> = repo.saveUserToken()

}

回购

class UserRepo {

    suspend fun saveUserToken(): Resource<Boolean> {
        FirebaseInstanceId.getInstance().instanceId
            .addOnCompleteListener(OnCompleteListener { task ->
                if (!task.isSuccessful) {
                    Log.w("saveUserToken", "getInstanceId failed", task.exception)
                    return@OnCompleteListener
                }

                // Get new Instance ID token
                val token = task.result?.token

            })

        //Here I need to return the Resource<Boolean> but wait untill it completes
    }
}

这是我的两个帮手类我用

资源

data class Resource<out T>(val status: Status, val data: T?, val message: String?) {

    companion object {
        fun <T> success(data: T?): Resource<T> {
            return Resource(Status.SUCCESS, data, null)
        }

        fun <T> error(msg: String, data: T?): Resource<T> {
            return Resource(Status.ERROR, data, msg)
        }

        fun <T> loading(data: T?): Resource<T> {
            return Resource(Status.LOADING, data, null)
        }
    }
}

状态

enum class Status {
    SUCCESS,
    ERROR,
    LOADING
}

当需要将对象传递给其他人时,我总是被困在存储库中 类,传递此流程的最佳方法是什么?

我知道如果我需要继续监听我应该使用 Flow with Coroutines,但现在我只需要一次操作就可以将这个对象跨越所有类传送到我的视图

谢谢

将存储库对象 return LiveData 完全有效。那就是我会做的。我认为您不应该尝试使它 return 同步,因为这完全违背了协程的目的。

如果您想将 Task 与协程一起使用,请考虑使用此库将播放服务 Task 对象转换为可以在挂起乐趣中等待的对象:

https://github.com/Kotlin/kotlinx.coroutines/tree/master/integration/kotlinx-coroutines-play-services

您可以从 Activity 或 Fragment 调用 login 没有任何问题,然后您应该更新您的 MutableLiveData 以使您的 Activity 知道更改。

我为从用户存储库和 Firebase Auth 检索登录所做的工作是:

ClassViewModel.kt

class LoginFirebaseViewModel(): ViewModel(){
    private val _loginResult = MutableLiveData<LoginResult>()
    val loginResult: LiveData<LoginResult> = _loginResult

    fun login() {
        viewModelScope.launch {
            try {
                repository.userLogin(email!!,password!!).let {
                    _loginResult.value = it
                }
            } catch (e: FirebaseAuthException) {
                // Do something on firebase exception
            }       
        }
    }
}

UserRepository.kt

class UserRepository(private val firebaseAuth: FirebaseAuth) {

    suspend fun userLogin(email: String, password: String) : LoginResult{
        val firebaseUser = firebaseAuth.signInWithEmailAndPassword(email, password).await()  // Do not forget .await()
        return LoginResult(firebaseUser)
    }
}

LoginResult 是 firebase 身份验证响应的包装器 class。

希望对你有所帮助