Reputation: 28845
I have a project with Kotlin coroutines and Retrofit.
I had these dependencies:
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'
Today I have updated Retrofit to 2.6.0 in the project. In https://github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter it is written that it is deprecated now. In https://github.com/square/retrofit/blob/master/CHANGELOG.md#version-260-2019-06-05 it is written that Retrofit currently supports suspend
.
So, I removed retrofit2-kotlin-coroutines-adapter:0.9.2
and in Retrofit client changed these lines:
retrofit = Retrofit.Builder()
.baseUrl(SERVER_URL)
.client(okHttpClient)
.addConverterFactory(MyGsonFactory.create(gson))
//.addCallAdapterFactory(CoroutineCallAdapterFactory()) - removed it.
.build()
When run, the first request catches an exception:
java.lang.IllegalArgumentException: Unable to create call adapter for kotlinx.coroutines.Deferred<com.package.model.response.UserInfoResponse>
for method Api.getUserInfo
As I understood, instead of CoroutineCallAdapterFactory()
I could use CallAdapter.Factory()
, but it is abstract.
If in Api class I change a request adding suspend
in the beginning:
@FormUrlEncoded
@POST("user/info/")
suspend fun getUserInfo(@Field("token") token: String): Deferred<UserInfoResponse>
override suspend fun getUserInfo(token: String): Deferred<UserInfoResponse> =
service.getUserInfo(token)
I get this exception:
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.
Upvotes: 30
Views: 16918
Reputation: 1012
This error may persist even after adding the suspend
keyword to your method. It may happen that calls with suspend
functions are not supported by your Retrofit version. Upgrade your Retrofit version to 2.6.0
or greater for suspend
functions support.
Upvotes: 0
Reputation: 28845
Reading https://github.com/square/retrofit/blob/master/CHANGELOG.md#version-260-2019-06-05 I saw:
New: Support suspend modifier on functions for Kotlin! This allows you to express the asynchrony of HTTP requests in an idiomatic fashion for the language.
@GET("users/{id}") suspend fun user(@Path("id") long id): User
Behind the scenes this behaves as if defined as fun user(...): Call and then invoked with Call.enqueue. You can also return Response for access to the response metadata.
Currently this integration only supports non-null response body types. Follow issue 3075 for nullable type support.
I changed requests so: added suspend
and removed Deferred
:
@FormUrlEncoded
@POST("user/info/")
suspend fun getUserInfo(@Field("token") token: String): UserInfoResponse
override suspend fun getUserInfo(token: String): UserInfoResponse =
service.getUserInfo(token)
Then in interactor (or simply when called the method getUserInfo(token)
) removed await()
:
override suspend fun getUserInfo(token: String): UserInfoResponse =
// api.getUserInfo(token).await() - was before.
api.getUserInfo(token)
UPDATE
Once I encountered a situation when downloading PDF files required removing suspend
in Api class. See How to download PDF file with Retrofit and Kotlin coroutines?.
Upvotes: 35
Reputation: 8713
In my case I was missing the CoroutineCallAdapterFactory
in my Retrofit initialization. Retrofit v2.5.0
Before:
val retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(httpClient)
.addConverterFactory(MoshiConverterFactory.create())
.build()
After: (working code)
val retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(httpClient)
.addConverterFactory(MoshiConverterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.build()
Upvotes: 16