首页 > 解决方案 > Kotlin 没有被视图模型调用

问题描述

我正在尝试打电话

override suspend fun getLoginResponse(loginRequest: LoginRequest) = flow {
    emit(ApiResult.Loading)
    networkCall {
        loginService.postLoginResponse(loginRequest)
    }.let { apiResult->
        apiResult.isSuccessAndNotNull().letOnTrueOnSuspend {
            (apiResult.getResult() as? LoginResponse)?.let {
                emit(ApiResult.Success(it))
                Timber.d(it.toString())
            } ?: run {  emit(ApiResult.Error(TypeCastException("unknown error.")))
                Timber.d(TypeCastException("unknown error."))}
        }
    }
}.flowOn(Dispatchers.IO)

从我的 viewModel 是这样的:

private fun loginResponse(email: String, password: String, device: String){
    viewModelScope.launch {
        try {
            var loginRequest = LoginRequest(email, password, device)
            loginResponseFromServer = loginRepository.getLoginResponse(loginRequest)
                .asLiveData(viewModelScope.coroutineContext+Dispatchers.Default)
            Timber.d(loginResponseFromServer.toString())
        }
        catch (e: NetworkErrorException){
            validationError.value = "Network communication error!"
        }
    }
}

当我调试或运行代码时getLoginResponse甚至没有调用。有什么我想念的吗?

标签: kotlinkotlin-coroutines

解决方案


首先,getLoginResponse不需要是挂起函数,因为它只是返回一个冷流。如果删除suspend修饰符,则不需要协程来调用它或将其转换为 LiveData。

其次,构建的 LiveData 在.asLiveData()第一次激活之前不会开始收集 Flow(保持冷态)。这是该函数的文档。当它接收到第一个观察者时,它变得活跃,但你的代码还没有开始观察它,这就是为什么你的flow块中的代码永远不会被调用的原因。

您也不需要为 LiveData 指定不同的调度程序。您收集哪个调度程序并不重要,因为收集它不会阻塞代码。

但是,LiveData 不应该在 ViewModel 中收集。它用于 UI 交互。应该从 Fragment 观察 LiveData。

您需要将捕获的网络异常转移到您的flow构建器中。在创建 Flow 或 LiveData 时不会抛出异常,而是在发出请求时(在 Flow 的执行中)。

我不确定如何重写您的流程构建器以正确捕获,因为它具有我没见过的功能。只是一个提示,但是将许多范围函数链接到一个语句中会使代码难以阅读和推理。

因此,要作为 LiveData 执行此操作,您可以按如下方式更改代码:

private fun loginResponse(email: String, password: String, device: String): LiveData<LoginResponse> {
    val loginRequest = LoginRequest(email, password, device)
    return loginRepository.getLoginResponse(loginRequest)
        .asLiveData()
}

然后在你的 Fragment 中观察它。

然而

LiveData 和 Flow 并不真正适合这个用例,因为您想发出一个请求并获得一个响应。您的存储库应该只公开一个返回响应的挂起函数。然后你的 ViewModel 可以有一个挂起函数,它通过调用存储库的挂起函数来传递响应。


推荐阅读