Kotlin协程中如何等待Retrofit的getData()请求执行完成
核心问题
你现在的写法存在逻辑冲突:loadData()是同步返回值的函数,但内部的Retrofit网络请求是通过viewModelScope.launch启动的异步协程任务,函数执行到return cryptoDatas的时候协程里的网络请求还未执行完成,自然拿不到最新的请求结果。
推荐改造方案
方案1:采用响应式编程逻辑,通过LiveData回调结果(最优适配现有代码)
你已经定义了currency这个LiveData存储请求结果,完全不需要让loadData()同步返回值,后续要执行的操作直接观察currency的变化即可:
首先改造loadData函数,去掉无意义的返回值,简化冗余的线程切换代码:
fun loadData() { val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() .create(CryptoAPI::class.java) viewModelScope.launch { // Retrofit挂起函数自动支持后台调度,不需要手动切Dispatchers.IO val response = retrofit.getData() // viewModelScope默认运行在主线程,不需要手动切Dispatchers.Main if (response.isSuccessful){ response.body()?.let { cryptoDatas = ArrayList(it) currency.value = cryptoDatas progressBarStatus.value = false println(response.isSuccessful) } } else { println(response.isSuccessful) // 可自行增加错误状态LiveData回调 } } }
然后在你需要执行后续操作的地方(比如Activity/Fragment),观察currency的变化,拿到结果后再执行后续逻辑:
// 示例代码,写在Activity/Fragment的onCreate/onViewCreated中 viewModel.currency.observe(this) { cryptoList -> // 这里拿到请求返回的结果,直接执行你需要的后续操作 }
方案2:如果确实需要同步等待请求结果再返回,将loadData改为挂起函数
如果你的业务场景需要函数直接返回请求结果,把loadData改成挂起函数,去掉内部的launch包装,直接执行请求:
suspend fun loadData(): ArrayList<CryptoData>? { val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() .create(CryptoAPI::class.java) val response = retrofit.getData() return if (response.isSuccessful) { response.body()?.let { cryptoDatas = ArrayList(it) currency.postValue(cryptoDatas) progressBarStatus.postValue(false) println(response.isSuccessful) cryptoDatas } } else { println(response.isSuccessful) null } }
调用这个挂起函数时,在协程作用域中调用即可,调用处可以自然等待执行完成再执行后续代码:
// 示例调用,写在ViewModel或者Activity/Fragment的协程中 viewModelScope.launch { val result = loadData() // 这里的result就是请求结果,直接执行后续操作 }
之前尝试不生效的原因
runBlocking会阻塞当前线程,如果你在主线程调用会直接卡住UI,不符合Android开发规范async{}.await()之前未生效是因为你没有把loadData改成挂起函数,外层还是同步返回,自然拿不到异步执行的结果
内容的提问来源于stack exchange,提问作者dltayn
相关产品推荐
相关产品推荐

