如何在Kotlin中从回调返回值并将结果传递至ViewModel?
解决方案1:用协程挂起函数转换回调
把回调式的异步API包装成挂起函数,就能在ViewModel的协程作用域里同步处理结果,这是最符合Kotlin协程生态的写法:
1. 包装回调为挂起函数
在你的API封装类/Repository中,用suspendCancellableCoroutine将回调转换为挂起函数:
suspend fun signInWithABC(): Result<OAuthToken> = suspendCancellableCoroutine { continuation -> abcApi.signIn { token, error -> val result = when { error != null -> Result.failure(error) token != null -> Result.success(token) else -> Result.failure(IllegalStateException("Token和Error均为空")) } continuation.resume(result) } // 可选:如果API支持取消,这里处理协程取消时的资源释放 continuation.invokeOnCancellation { // abcApi.cancelSignIn() // 若API提供取消方法,在此调用 } }
2. 在ViewModel中调用
借助viewModelScope启动协程,直接获取并处理结果:
class AuthViewModel : ViewModel() { // 用StateFlow暴露UI状态给界面 val authState = MutableStateFlow<AuthState>(AuthState.Idle) fun handleSignIn() { viewModelScope.launch { authState.value = AuthState.Loading val result = signInWithABC() result.fold( onSuccess = { token -> authState.value = AuthState.Success(token) // 处理成功逻辑:如保存token到DataStore、跳转到首页等 }, onFailure = { error -> authState.value = AuthState.Error(error.message ?: "登录失败") // 处理错误逻辑:如提示用户、记录日志等 } ) } } } // 定义UI状态密封类,规范状态类型 sealed class AuthState { object Idle : AuthState() object Loading : AuthState() data class Success(val token: OAuthToken) : AuthState() data class Error(val message: String) : AuthState() }
解决方案2:用StateFlow直接传递结果
如果不想用挂起函数,也可以在封装类中定义StateFlow,回调触发时更新Flow,ViewModel收集状态变化:
1. 在封装类中定义StateFlow
class AuthRepository { private val _signInResult = MutableStateFlow<Result<OAuthToken>?>(null) // 对外暴露只读的StateFlow val signInResult: StateFlow<Result<OAuthToken>?> = _signInResult fun signInWithABC() { abcApi.signIn { token, error -> val result = when { error != null -> Result.failure(error) token != null -> Result.success(token) else -> Result.failure(IllegalStateException("Token和Error均为空")) } _signInResult.value = result } } }
2. 在ViewModel中收集Flow
class AuthViewModel(private val authRepo: AuthRepository) : ViewModel() { val authState = MutableStateFlow<AuthState>(AuthState.Idle) init { // 监听登录结果的变化 viewModelScope.launch { authRepo.signInResult.collect { result -> result?.let { authState.value = when { it.isSuccess -> AuthState.Success(it.getOrThrow()) else -> AuthState.Error(it.exceptionOrNull()?.message ?: "登录失败") } } } } } fun handleSignIn() { authState.value = AuthState.Loading authRepo.signInWithABC() } }
为什么你的原有写法不可行?
你尝试直接返回Result是忽略了异步操作的本质:abcApi.signIn是异步执行的,函数调用后会立刻返回,此时回调还未触发,根本拿不到结果。必须通过协程(挂起函数)或数据流(StateFlow/LiveData)来处理异步结果的传递。
内容的提问来源于stack exchange,提问作者c-an
相关产品推荐
相关产品推荐

