Android Kotlin嵌套挂起函数使用:确保回调完成后再返回结果
解决协程中异步回调先于return执行的问题
嘿,这个问题我太熟悉了!你遇到的问题本质是**enqueue()是异步回调模式,它不会挂起当前协程**——也就是说,你调用call.enqueue()后,代码会立刻往下执行return@withContext那一行,完全不等网络请求的回调触发,自然拿不到正确的结果;而且你原来的isJobSuccess默认是false,就算回调执行了,成功分支里也没把它改成true,逻辑本身也有问题。
最优解决方案:改用Retrofit的挂起函数
现在Retrofit已经原生支持协程挂起函数了,完全没必要再用回调式的enqueue(),直接把接口改成挂起函数,让协程自动等待网络请求完成。
第一步:修改你的ApiInterface
把原来返回Call<ResponseBody?>的方法改成挂起函数,直接返回Response<ResponseBody>(保留Response能拿到状态码等完整响应信息):
interface ApiInterface { // 去掉Call包装,加上suspend关键字 suspend fun downloadDictionaryFile(@Url url: String): Response<ResponseBody> }
第二步:重写doWorkerJob逻辑
现在可以直接在协程里调用挂起函数,协程会自动挂起直到请求完成,这样就能保证结果判断的代码在请求结束后执行:
class DictionaryWorker constructor( context: Context, workerParameters: WorkerParameters, private val apiInterface: ApiInterface ) : KneuraWorker(context, workerParameters), BaseDataSource { override suspend fun doWorkerJob(): Result = withContext(Dispatchers.IO) { return@withContext try { // 调用挂起函数,协程会等待请求完成后再继续执行 val response = apiInterface.downloadDictionaryFile(DICTIONARY_FILE_URL) if (response.isSuccessful) { // 这里添加下载成功后的逻辑,比如把文件保存到本地 Log.d("TAG", "dictionary download success") Result.success() } else { Log.d("TAG", "server contact failed, code: ${response.code()}") Result.failure() } } catch (t: Throwable) { // 捕获网络异常、IO异常等所有请求失败场景 Log.d("TAG", "download failed due to exception", t) Result.failure() } } }
如果你暂时不想改接口(兼容老代码)
如果因为某些原因不能修改ApiInterface,可以用Retrofit提供的await()扩展函数把Call转换成挂起调用(新版本Retrofit已内置该扩展):
override suspend fun doWorkerJob(): Result = withContext(Dispatchers.IO) { return@withContext try { val call = apiInterface.downloadDictionaryFile(DICTIONARY_FILE_URL) // 用await()挂起协程,等待请求完成后拿到响应 val response = call.await() if (response.isSuccessful) { Log.d("TAG", "dictionary download success") Result.success() } else { Log.d("TAG", "server contact failed, code: ${response.code()}") Result.failure() } } catch (t: Throwable) { Log.d("TAG", "download failed due to exception", t) Result.failure() } }
关键原理
协程的核心优势之一就是把异步回调转换成同步写法,通过挂起函数让代码“看起来是同步执行,但实际是异步非阻塞的”,这样就彻底避免了回调地狱和异步时序混乱的问题。你原来的写法把回调和协程混用,相当于两个异步体系冲突了,所以才会出现时序错误。
内容的提问来源于stack exchange,提问作者Devrath
相关产品推荐
相关产品推荐

