背景
在使用Kotlin coroutines结合Retrofit获取网络数据时,出现如下报错:
2019-04-16 15:24:47.210 20471-20471/com.cxyzy.note E/RepoViewModel$getRepo: java.lang.IllegalArgumentException: Unable to create call adapter for kotlinx.coroutines.Deferred
for method Api.repos
at retrofit2.Utils.methodError(Utils.java:52)
at retrofit2.HttpServiceMethod.createCallAdapter(HttpServiceMethod.java:60)
at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:34)
at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:36)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:168)
at retrofit2.Retrofit$1.invoke(Retrofit.java:147)
at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
at $Proxy1.repos(Unknown Source)
原关键代码:
- 构造Retrofit接口代码
Retrofit.Builder()
.baseUrl("https://api.github.com")
.client(provideOkHttpClient(provideLoggingInterceptor()))
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.build()
.create(Api::class.java)
- Api定义
interface Api {
@GET("/users/{user}/repos")
fun repos(@Path("user") user: String, @Query("page") page: Int, @Query("per_page") perPage: Int): Deferred
}
简介方案
- 增加CoroutineCallAdapterFactory
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import retrofit2.*
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
class CoroutineCallAdapterFactory private constructor() : CallAdapter.Factory() {
companion object {
@JvmStatic
@JvmName("create")
operator fun invoke() = CoroutineCallAdapterFactory()
}
override fun get(
returnType: Type,
annotations: Array,
retrofit: Retrofit
): CallAdapter? {
if (Deferred::class.java != getRawType(returnType)) {
return null
}
if (returnType !is ParameterizedType) {
throw IllegalStateException(
"Deferred return type must be parameterized as Deferred or Deferred")
}
val responseType = getParameterUpperBound(0, returnType)
val rawDeferredType = getRawType(responseType)
return if (rawDeferredType == Response::class.java) {
if (responseType !is ParameterizedType) {
throw IllegalStateException(
"Response must be parameterized as Response or Response")
}
ResponseCallAdapter(getParameterUpperBound(0, responseType))
} else {
BodyCallAdapter(responseType)
}
}
private class BodyCallAdapter(
private val responseType: Type
) : CallAdapter {
override fun responseType() = responseType
override fun adapt(call: Call): Deferred {
val deferred = CompletableDeferred()
deferred.invokeOnCompletion {
if (deferred.isCancelled) {
call.cancel()
}
}
call.enqueue(object : Callback {
override fun onFailure(call: Call, t: Throwable) {
deferred.completeExceptionally(t)
}
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
deferred.complete(response.body()!!)
} else {
deferred.completeExceptionally(HttpException(response))
}
}
})
return deferred
}
}
private class ResponseCallAdapter(
private val responseType: Type
) : CallAdapter {
override fun responseType() = responseType
override fun adapt(call: Call): Deferred {
val deferred = CompletableDeferred()
deferred.invokeOnCompletion {
if (deferred.isCancelled) {
call.cancel()
}
}
call.enqueue(object : Callback {
override fun onFailure(call: Call, t: Throwable) {
deferred.completeExceptionally(t)
}
override fun onResponse(call: Call, response: Response) {
deferred.complete(response)
}
})
return deferred
}
}
}
- 修改构造Retrofit接口代码,增加callAdapterFactory:
addCallAdapterFactory(CoroutineCallAdapterFactory())
Retrofit.Builder()
.baseUrl("https://api.github.com")
.client(provideOkHttpClient(provideLoggingInterceptor()))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.build()
.create(Api::class.java)