观察回调没有被 Kotlin 协程 Flow 和 LiveData 触发?

Observe callback is not being triggered with Kotlin coroutines Flow and LiveData?

我是 Android 开发的新手,正在尝试从各种示例项目中了解协程和 LiveData。我目前已经设置了一个函数来在用户输入用户名和密码时调用我的api。然而,在按下 1 个按钮后,应用程序似乎卡住了,我无法再进行 api 调用,就好像它卡在了一个挂起的进程中一样。

这是我的第一个 android 应用,融合了很多想法,所以请让我知道哪里出错了!

Activity:

binding.bLogin.setOnClickListener {
        val username = binding.etUsername.text.toString()
        val password = binding.etPassword.text.toString()
        viewModel.userClicked(username, password).observe(this, Observer {
            it?.let { resource ->
                when (resource.status) {
                    Status.SUCCESS -> {
                        print(resource.data)
                    }
                    Status.ERROR -> {
                        print(resource.message)
                    }
                    Status.LOADING -> {
                        // loader stuff
                    }
                }
            }
        })
 }

视图模型:

fun userClicked(username: String, password: String) = liveData(dispatcherIO) {
    viewModelScope.launch {
        emit(Resource.loading(data = null))
        try {
            userRepository.login(username, password).apply {
                emit(Resource.success(null))
            }
        } catch (exception: Exception) {
            emit(Resource.error(exception.message ?: "Error Occurred!", data = null))
        }
    }
}

存储库:

@WorkerThread
suspend fun login(
    username: String,
    password: String
): Flow<Resource<String?>> {
    return flow {
        emit(Resource.loading(null))
        api.login(LoginRequest(username, password)).apply {
            this.onSuccessSuspend {
                data?.let {
                    prefs.apiToken = it.key
                    emit(Resource.success(null))
                }
            }
        }.onErrorSuspend {
            emit(Resource.error(message(), null))
        }.onExceptionSuspend {
            emit(Resource.error(message(), null))
        }
    }.flowOn(dispatcherIO)
}

API:

suspend fun login(@Body request: LoginRequest): ApiResponse<Auth>

您不需要在 liveData 构建器中启动协程,它已经 suspend 因此您可以在那里调用 suspend 函数:

fun userClicked(username: String, password: String) = liveData(dispatcherIO) {
    emit(Resource.loading(data = null))
    try {
        userRepository.login(username, password).apply {
            emit(Resource.success(null))
        }
    } catch (exception: Exception) {
        emit(Resource.error(exception.message ?: "Error Occurred!", data = null))
    }
}

如果您想将 LiveDateFlow 一起使用,您可以使用 asLiveData 函数将 Flow 转换为 LiveData 对象:

fun userClicked(username: String, password: String): LiveData<Resource<String?>> {
    return userRepository.login(username, password).asLiveData()
}

但我不建议在项目中混用 LiveDataFlow 流。我建议只使用 Flow.

仅使用 Flow:

// In ViewModel:

fun userClicked(username: String, password: String): Flow<Resource<String?>> {
    return userRepository.login(username, password)
}

// Activity

binding.bLogin.setOnClickListener {
        val username = binding.etUsername.text.toString()
        val password = binding.etPassword.text.toString()
        lifecycleScope.launch {
            viewModel.userClicked(username, password).collect { resource ->
                when (resource.status) {
                    Status.SUCCESS -> {
                        print(resource.data)
                    }
                    Status.ERROR -> {
                        print(resource.message)
                    }
                    Status.LOADING -> {
                        // loader stuff
                    }
                }
            }
        }
    }

Repository 中的 login 函数中删除 suspend 关键字。

lifecycleScope docs.