I am trying to understand how retrofit implements the following suspend function:
#GET("api/me")
suspend fun getUser(): User
Is there anyway that I can access the locally generated implementation code?
Thanks!
According to docs,
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.
Everything else lies inside the sources like this class. Btw, I'm just curious, why do you need to understand the exact implementation from the Retrofit side?
Related
I'm using retrofit to generate the POST requests in my simple API call:
interface IRetrofitCommService {
#POST("pushnotifications/{entityId}")
suspend fun getNotificationsAsync(
#Path("entityId") entityId: Long,
#Body model: GetNotificationsDto
): Response<List<NotificationData>>
#POST("pushnotifications")
suspend fun registerDeviceAsync(#Body model: RegisterEntityDto):
Response<RegisterEntityResultDto>
}
Note that in the second call, I have only 1 parameter, which is marked with the #Body annotation.
Yet, when I try to use the web call, I get this exception: No annotation found for param 2
Here is my factory for creating the call:
object RetrofitFactory {
const val BASE_URL = "https://localhost:5051/api/"
fun createService(): IRetrofitCommService {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(IRetrofitCommService::class.java)
}
}
Here is the DTO in question:
data class RegisterEntityDto(val name: String, val eventType: Short, val clientId: String)
Why is it looking for a 2nd parameter then? What am I missing?
I'm not very familiar with the Retrofit and I'm doing an educated guess here, but after a short discussion in comments it seems my understanding is actually correct.
Internally, suspend functions in Kotlin receive additional Continuation parameter. It is always the last parameter and it is hidden from the Kotlin code. However, if we look into the generated bytecode, we'll see registerDeviceAsync() function actually receives 2 parameters.
If some tool we use is not aware of suspend functions, it won't be able to properly interpret this additional parameter. It will just "think" registerDeviceAsync() has two params. Retrofit added support for suspend functions in version 2.6.0, so I guess if using older versions with suspend functions we will get exactly the behavior you faced.
You just need to update Retrofit to newer version.
I am learning retrofit and cannot understand what is Call<> in that means.
Even the docs are too hard to understand.
Can someone give a clear explanation on this?
Think of Call as a simple class which wraps your API response and you need this class make an API call and provide listeners/callback to notify you with error and response , although if you use kotlin coroutines then after version 2.6.0 or retrofit you can totally abandon Call , you can directly return response from the function and you don't need any callback which is very clean.
do if like
#GET("users/{id}")
suspend fun user(#Path("id") id: Long): User
or
#GET("users/{id}")
suspend fun user(#Path("id") id: Long): Response<User>
Call is a method to request to the webserver/API to get data.(Based on my understanding)
I have a problem using retrofit with rxandroid.
I've created a custom converter as below:
class CustomResponseConverter<T>(private val converter: Converter<ResponseBody, *>): Converter<ResponseBody, T> {
override fun convert(value: ResponseBody): T? {
// custom convert response here
}
}
It's all working fine when i'm returning Single like this:
#GET("route")
fun simpleFetch(): Single<FetchData>
but when i try returning Completable like this:
#GET("route")
fun simpleFetch(): Completable
I found that the convert function doesn't get call. Please help.
Thanks in advance.
For anyone who's running into the same case as me, apparently according to retrofit team here:
Using Completable bypasses all converters, yes, and simply closes the response body so it is consumed. Since there is nowhere for the converted body to go with a Completable, there is no need to call it and perform conversion.
So I guess we'll just keep using Single on this case.
In Android sample codes/codelabs, Daos inside RoomDatabase are defined as
abstract fun genericDao(): GenericDao
and when you need to access the dao's methods, you call
database.genericDao().genericFun()
Why is it implemented like this, and not like
abstract val genericDao: GenericDao
database.genericDao.genericFun()
? Is it wrong to do it the second way?
Link to Codelab
Ok, i checked Decompiled bytecode and the only difference is that first implementation is named as
subscriptionStatusDao()
and the second one as
getSubscriptionStatusDao()
So basically, no difference at all.
I'm trying to use Coroutines with a Room database in an Android project. I've found almost no documentation online, and I'm wondering if it is possible to return Deferred<> types in those methods. Something like this:
#Dao
interface MyObjectDAO {
#Query("SELECT * FROM myObject WHERE id_myObject = :idMyObject")
suspend fun readMyObjectAsync(idMyObject: Int): Deferred<MyObject>
}
I've tried this and I get Not sure how to convert a Cursor to this method's return type at compile time.
My dependencies are:
kapt 'androidx.room:room-compiler:2.1.0-alpha04'
implementation 'androidx.room:room-runtime:2.1.0-alpha04'
implementation 'androidx.room:room-coroutines:2.1.0-alpha04'
Your issue lies in that you're mixing the suspending converter and the Deferred converter. Use one or the other and your code will work as intended.
fun readMyObjectAsync(idMyObject: Int): Deferred<MyObject> - Best choice if you need to interface/be compatible with java code, since it doesn't require code transformations to actually function.
suspend fun readMyObjectAsync(idMyObject: Int): MyObject - If you're operating on pure kotlin this will allow better control through the context it is called in.