使用Retrofit KotlinExtensions结合协程时遇调用问题
Retrofit协程调用接口报错:无法创建Call转换器及await()引用问题
你的问题根源在于Retrofit中suspend函数的返回值定义错误,以及对Retrofit协程扩展的用法理解有误,以下是具体分析和修正方案:
核心错误点
1. 错误的suspend函数返回值定义
你在ApiInterface中将suspend方法的返回值设为Call<Data>,这完全违背了Retrofit对协程的设计逻辑:
- Retrofit的suspend函数会自动处理异步调用流程,直接返回最终的响应数据类型(如
Data),不需要再用Call包装。 - 当你定义
suspend fun fetchSomeInfo(): Call<Data>时,Retrofit会尝试把接口响应转换成Call<Data>类型,但你的Moshi转换器只能处理Data对象,因此会抛出Unable to create converter for retrofit2.Call<Data>异常。
2. await()扩展函数的适用场景
Retrofit的await()是Call<T>的专属扩展函数,仅用于非suspend的传统Call方法,比如:
interface ApiInterface { @GET("api/fetch-some-info") fun fetchSomeInfo(@Query("some_query_param") queryParam: String): Call<Data> }
这种非suspend方法返回的Call<Data>,才能通过call.await()在协程中获取结果。而suspend方法直接返回数据,不需要额外调用await()。
修正方案
第一步:修正ApiInterface定义
将suspend方法的返回值改为直接返回Data:
interface ApiInterface { @GET("api/fetch-some-info") suspend fun fetchSomeInfo( @Query("some_query_param") queryParam: String, ): Data }
第二步:正确的协程调用(带Job引用可取消)
如果你需要保留Job引用以便后续取消请求,直接在协程中调用suspend方法即可,无需额外用async包裹(除非需要并行执行多任务):
// 建议使用带生命周期的Scope,比如ViewModelScope,避免内存泄漏 val requestScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) var requestJob: Job? = null // 发起请求 requestJob = requestScope.launch { try { val data = api.fetchSomeInfo("Param") // 处理返回的data } catch (e: Exception) { // 处理异常:网络错误、数据解析错误等 } } // 后续需要取消请求时 requestJob?.cancel()
关于你能正常运行的写法说明
你提到的这段代码能正常运行,说明你实际使用的ApiInterface中,fetchSomeInfo方法并没有返回Call<Data>,而是直接返回Data:
var call : Deferred<Data>? = null MainScope().launch { call = async(Dispatchers.IO) { api.fetchSomeInfo("Param") } call?.await().let {//handle response}
这里async包裹的是返回Data的suspend方法,生成的Deferred<Data>可以调用await(),但这种写法没必要用async,直接在launch协程中调用suspend方法更简洁高效。
内容的提问来源于stack exchange,提问作者Engo
相关产品推荐
相关产品推荐

