Unable to create call adapter for kotlinx.coroutines.Deferred
for to create unable call Adapter deferred
2023-09-27 14:27:30 时间
背景
在使用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<java.util.List<com.cxyzy.note.network.bean.Repo>>
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<List<Repo>>
}
简介方案
- 增加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<out Annotation>,
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<Foo> or Deferred<out Foo>")
}
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<Foo> or Response<out Foo>")
}
ResponseCallAdapter<Any>(getParameterUpperBound(0, responseType))
} else {
BodyCallAdapter<Any>(responseType)
}
}
private class BodyCallAdapter<T>(
private val responseType: Type
) : CallAdapter<T, Deferred<T>> {
override fun responseType() = responseType
override fun adapt(call: Call<T>): Deferred<T> {
val deferred = CompletableDeferred<T>()
deferred.invokeOnCompletion {
if (deferred.isCancelled) {
call.cancel()
}
}
call.enqueue(object : Callback<T> {
override fun onFailure(call: Call<T>, t: Throwable) {
deferred.completeExceptionally(t)
}
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
deferred.complete(response.body()!!)
} else {
deferred.completeExceptionally(HttpException(response))
}
}
})
return deferred
}
}
private class ResponseCallAdapter<T>(
private val responseType: Type
) : CallAdapter<T, Deferred<Response<T>>> {
override fun responseType() = responseType
override fun adapt(call: Call<T>): Deferred<Response<T>> {
val deferred = CompletableDeferred<Response<T>>()
deferred.invokeOnCompletion {
if (deferred.isCancelled) {
call.cancel()
}
}
call.enqueue(object : Callback<T> {
override fun onFailure(call: Call<T>, t: Throwable) {
deferred.completeExceptionally(t)
}
override fun onResponse(call: Call<T>, response: Response<T>) {
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)
相关文章
- ognl.OgnlException: target is null for setProperty(null,"XXXX"...)
- [转] JavaScript中in操作符(for..in)、Object.keys()和Object.getOwnPropertyNames()的区别
- GZIP in .net core not working Unable to resolve service for type 'Microsoft.AspNetCore.ResponseCompression.IResponseCompressionProvider'
- How to check for a valid Base64 encoded string
- The CLR has been unable to transition from COM context […] for 60 seconds
- vue - utils for extract-text-webpack-plugin
- spark报Got an error when resolving hostNames. Falling back to /default-rack for all
- org.apache.jasper.JasperException: Unable to compile class for JSP
- Mysql连接错误:Lost connection to Mysql server at 'waiting for initial communication packet'
- uthentication to host '10.0.1.33' for user 'root' using method 'mysql_native_password' failed with message: Access denied for user 'root'@'PCName' (using password: YES)
- TP:DD4FAE06 Thanks for TexturePacker
- clj-xmemcached: memcached client for clojure
- Failed to read artifact descriptor for xxx:jar的问题解决(转载)
- 【HTTP】Speed and Mobility: An Approach for HTTP 2.0 to Make Mobile Apps and the Web Faster
- 知识驱动对话-Learning to Select Knowledge for Response Generation in Dialog Systems-阅读笔记
- 每日一篇文献:Virtual Kinesthetic Teaching for Bimanual Telemanipulation
- How LinkedIn customizes Apache Kafka for 7 trillion messages per day
- found 12 vulnerabilities (7 moderate, 5 high) run `npm audit fix` to fix them, or `npm audit` for details
- Install Typora For Linux
- 天津政府应急系统之GIS一张图(arcgis api for flex)讲解(十二)水情雨情模块
- 天津政府应急系统之GIS一张图(arcgis api for flex)讲解(七)地图打印模块
- SpringSecurity常见报错处理:Can't configure antMatchers after anyRequest、There is no PasswordEncoder mapped for the id "null"
- 浅析 vite.config.js 常见基础配置及本地打包解决Cross origin requests are only supported for protocol schemes的问题
- Hadoop: Setup Maven project for MapReduce in 5mn
- Creating Help Pages for ASP.NET Web API -摘自网络
- [LeetCode] 983. Minimum Cost For Tickets 最低票价
- forEarch 和 for in
- ERROR 1044 (42000): Access denied for user ''@'localhost' to database 'mysql'