Retrofit 2.6.0 异常: java.lang.IllegalArgumentException: 无法为 kotlinx.coroutines.Deferred 创建调用适配器

30

我有一个使用Kotlin协程和Retrofit的项目。

我使用了以下依赖项:

implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'

今天我已经将项目中的 Retrofit 更新到了 2.6.0。在 https://github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter 中写道它现在已过时。而在 https://github.com/square/retrofit/blob/master/CHANGELOG.md#version-260-2019-06-05 中写道,Retrofit 目前支持 suspend

因此,我移除了 retrofit2-kotlin-coroutines-adapter:0.9.2,并在 Retrofit 客户端更改了这些行:

        retrofit = Retrofit.Builder()
            .baseUrl(SERVER_URL)
            .client(okHttpClient)
            .addConverterFactory(MyGsonFactory.create(gson))
            //.addCallAdapterFactory(CoroutineCallAdapterFactory()) - removed it.
            .build()

运行时,第一个请求捕获异常:

java.lang.IllegalArgumentException: Unable to create call adapter for kotlinx.coroutines.Deferred<com.package.model.response.UserInfoResponse>
    for method Api.getUserInfo

据我所理解,我可以使用CallAdapter.Factory()代替CoroutineCallAdapterFactory(),但是它是抽象的。

如果在Api类中将请求开头添加suspend

@FormUrlEncoded
@POST("user/info/")
suspend fun getUserInfo(@Field("token") token: String): Deferred<UserInfoResponse>

override suspend fun getUserInfo(token: String): Deferred<UserInfoResponse> =
    service.getUserInfo(token)

我遇到了这个异常:

java.lang.RuntimeException: Unable to invoke no-args constructor for kotlinx.coroutines.Deferred<com.package.model.response.UserInfoResponse>. Registering an InstanceCreator with Gson for this type may fix this problem.
2个回答

35

阅读https://github.com/square/retrofit/blob/master/CHANGELOG.md#version-260-2019-06-05后,我看到:

新增:支持Kotlin函数上的suspend修饰符!这允许您以语言惯用方式表达HTTP请求的异步性。

@GET("users/{id}") suspend fun user(@Path("id") long id): User

在幕后,它的行为就像定义为fun user(...):Call,然后使用Call.enqueue调用。您还可以返回Response以访问响应元数据。

目前,此集成仅支持非空响应正文类型。有关可空类型支持,请参见问题3075。

我对请求进行了更改:添加了suspend并删除了Deferred

@FormUrlEncoded
@POST("user/info/")
suspend fun getUserInfo(@Field("token") token: String): UserInfoResponse


override suspend fun getUserInfo(token: String): UserInfoResponse =
    service.getUserInfo(token)

然后在交互器中(或者简单地在调用方法getUserInfo(token)时)移除了await()

override suspend fun getUserInfo(token: String): UserInfoResponse =
    // api.getUserInfo(token).await() - was before.
    api.getUserInfo(token)

更新

曾经遇到一个情况,在下载 PDF 文件时需要在 Api 类中删除 suspend。详见 如何使用 Retrofit 和 Kotlin 协程下载 PDF 文件?


2
我的回答简短明了,但你的回答包含了所有的来源和细节。你可以接受自己的回答。 - Eugen Pechanec
1
@EugenPechanec,不过,你做得很好,浪费了时间,而且你是第一个。至少,你应该得到+10。 - CoolMind
2
我不是为了分数而做这件事,没关系。我很感激你的举动。 - Eugen Pechanec
1
谢谢@CoolMind。你救了我一命! - ninehundreds
@Eugen Pechanec,你的回答在哪里? - The incredible Jan
@TheincredibleJan 你好,我删除了它,因为这个答案本质上是相同的,但更加完整。 - Eugen Pechanec

16
在我的情况下,我在Retrofit初始化中缺失了CoroutineCallAdapterFactory。 Retrofit v2.5.0
之前的代码如下:
val retrofit = Retrofit.Builder()
                .baseUrl(BuildConfig.BASE_URL)
                .client(httpClient)
                .addConverterFactory(MoshiConverterFactory.create())
                .build()

之后:(可运行的代码)

val retrofit = Retrofit.Builder()
                .baseUrl(BuildConfig.BASE_URL)
                .client(httpClient)
                .addConverterFactory(MoshiConverterFactory.create())
                .addCallAdapterFactory(CoroutineCallAdapterFactory())
                .build()

1
你使用的 Retrofit 版本是哪个? - CoolMind
@CoolMind 实现 'com.squareup.retrofit2:retrofit:2.5.0' - Nicola Gallazzi
3
啊,我明白了。那么,这就有道理了。但是我建议升级到2.6.0版本,这会让请求更加简单。 - CoolMind
1
顺便提一下,在你的问题中有2.5.0版本;无论如何,我刚刚测试了我的代码,它也适用于2.6.0版本。 - Nicola Gallazzi
2
哈哈,我正在从2.5.0升级到2.6.0,然后捕获了那个异常。目前一切正常,很惊讶它竟然能在2.6.0上工作。 - CoolMind
通过添加代码 .addCallAdapterFactory(CoroutineCallAdapterFactory()),已经进行了改进。 - Supun Ayeshmantha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接