I'm trying to get data using Retrofit call.enqueue ( DrawCircles() ), when i debug the values are there, but i think it does not waits and my function proceeds with the rest of lines of code. So the problem when I run it, the List of results (myListCoord ) is always null, how to make synchronous calls.
here is my code:
doAsync {
var a = DrawCircles()
myListCoord = a.runCircles()
}
fun runCircles(): List<Coordinates>? {
val request = ServiceBuilder.buildService(TmdbEndpoints::class.java)
val call = request.getCorrdinates()
call.enqueue(object : Callback<MyList> {
override fun onResponse(call: Call<MyList>, response: Response<MyList>) {
if (response.isSuccessful){
Toast.makeText(this#DrawCircles, "Succès", Toast.LENGTH_LONG).show()
myListCoord = response.body()!!.locations
}
}
override fun onFailure(call: Call<MyList>, t: Throwable) {
Toast.makeText(this#DrawCircles, "${t.message}", Toast.LENGTH_SHORT).show()
}
})
return myListCoord
}
Have you tried using call.execute() instead of call.enqueue() ?
From the docs:
void enqueue(Callback<T> callback)
Asynchronously send the request and notify callback of its response or if an error occurred talking to the server, creating the request, or processing the response.
Response<T> execute() throws IOException
Synchronously send the request and return its response.
(Emphasis mine)
Related
I want to create a REST-API between an Android client and a Spring Boot server.
I created an OpenAPI 3.0 specification and used the CLI generator from https://openapi-generator.tech to create client and server stubs.
The server part works as intended when accessing it with other clients.
For the client side I used the generator for Kotlin with Retrofit2 via the parameter --additional-properties=library=jvm-retrofit2.
What I get is:
A ModelApi interface, defining my endpoint
A Model class, containing my model
An infrastructure package, containing ApiClient, ResponseExt, Serializer, CollectionFormats and a few *Adapter classes
The generated model class (shortened):
data class MapModel (
#Json(name = "id")
val id: kotlin.Long? = null,
#Json(name = "description")
val desc: String? = null
)
The API interface:
interface MapModelApi {
#GET("mapModel")
fun mapModelGet(): Call<kotlin.collections.List<MapModel>>
#DELETE("mapModel/{mapModelId}")
fun mapModelMapModelIdDelete(#Path("mapModelId") mapModelId: kotlin.Int): Call<Unit>
#GET("mapModel/{mapModelId}")
fun mapModelMapModelIdGet(#Path("mapModelId") mapModelId: kotlin.Int): Call<MapModel>
#PUT("mapModel/{mapModelId}")
fun mapModelMapModelIdPut(#Path("mapModelId") mapModelId: kotlin.Int, #Body mapModel: MapModel): Call<Unit>
#POST("mapModel")
fun mapModelPost(#Body mapModel: MapModel): Call<Unit>
#PUT("mapModel")
fun mapModelPut(#Body mapModel: MapModel): Call<Unit>
}
To do a GET request on the element 0, i tried this in my Activity:
val apiClient = ApiClient()
val mapObjectService = apiClient.createService(MapModelApi::class.java)
val call = mapObjectService.mapModelMapModelIdGet(0)
call.enqueue(object : Callback<MapModel> {
override fun onFailure(
call: Call<MapModel>,
t: Throwable
) {
Log.v("retrofit", "call failed")
t.printStackTrace()
}
override fun onResponse(
call: Call<MapModel>,
response: Response<MapModel>
) {
if (response.isSuccessful) {
val mapModel = response.body()
println(mapModel?.id)
} else {
val statusCode = response.code()
println("Http Code: $statusCode")
}
}
})
When I execute this I get a response, but it is always a 501 response "Not Implemented".
How can I fix this? What is missing in the code?
The server is the problem. The GET request returned a body with example data. I have overseen, that the request code sent by the server was not 200, but 501.
I'm trying to create an Interceptor in the event that the API I'm using goes down, which has happened when I tried to make an API call on Postman only for it to return a 504 error.
This is the OkHttpClient I have for now. I set it to 5 seconds only for testing purposes.
val client = OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.addInterceptor(object : Interceptor {
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
val response = chain.proceed(chain.request())
when (response.code()) {
504 -> {
//Show Bad Request Error Message
}
}
return response
}
})
.build()
searchRetrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(URL)
.client(client)
.build()
Later in the code, I use Retrofit's execute() method to make a synchronous call to the API. The execute() line and val response = chain.proceed(chain.request()) crashes my app if the API service is down or if it's taking too long to retrieve results. I get the java.net.SocketTimeoutException error.
What can I do to prevent my app from crashing when the API service I'm using is down? What can I add to the Interceptor or should I surround my execute() call in a try catch statement?
Proper solution would be to use enqueue instead of execute. synchronous network calls are almost always a bad idea, because you don't want to block the calling thread. to use enqueue you should do
call.enqueue(object : Callback<SomeResponse> {
override fun onFailure(call: Call<SomeResponse>?, t: Throwable?) {
// This code will be called when your network call fails with some exception
// SocketTimeOutException etc
}
override fun onResponse(call: Call<SomeResponse>?, response: Response<SomeResponse>?) {
// This code will be called when response is received from network
// response can be anything 200, 504 etc
}
})
If you must use execute then at the very least you will have to enclose your execute call in try catch
try{
call.execute()
}
catch(e: Exception){
// Process any received exception (SocketTimeOutEtc)
}
I'm implementing Twitter OAuth flows as per:
https://developer.twitter.com/en/docs/authentication/guides/log-in-with-twitter
I am getting a response back for the first step (oauth/request_token) which has a 200 code, but the response body is completely empty.
I'm using Retrofit to call the API, and have hooked up an interceptor OkHttpClient to debug the response like so:
val client = OkHttpClient.Builder().also { builder ->
builder.addInterceptor { chain ->
val request = chain.request()
val response = chain.proceed(request)
response
}
}.build()
Then setting up Retrofit like so:
Retrofit.Builder()
.baseUrl(TWITTER_AUTH_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
.create(TwitterAuthRetrofit::class.java)
.getRequestToken(
authorizationHeaders
).enqueue(object : Callback<TwitterRequestToken> {
override fun onResponse(call: Call<TwitterRequestToken>, response: Response<TwitterRequestToken>) {
onSuccess(response.body())
}
override fun onFailure(call: Call<TwitterRequestToken>, t: Throwable) {
onFailure()
}
})
When I debug in the interceptor, I can see the response is successful (200) but the response body is empty, which I think is causing my Gson deserialization to fail.
The result of calling response.body.contentLength() in the interceptor is -1.
The result of calling response.code in the interceptor is 200.
Here is the model I am attempting to deserialize the response body to:
data class TwitterRequestToken(
#SerializedName(value = "oauth_token")
val token: String,
#SerializedName(value = "oauth_token_secret")
val tokenSecret: String,
#SerializedName(value = "oauth_callback_confirmed")
val callbackConfirmed: Boolean
)
Note I am using #SerializedName to provide the keys for the response body, whilst the names of my properties are arbitrary to our app (we use camel case). I add a GsonConverterFactory to the Retrofit instance using the builder and have done this in the same way for many other requests before with no issues.
Here is the response I am getting from the API, which I am looking at via debugging in the interceptor above:
Response{protocol=h2, code=200, message=, url=https://api.twitter.com/oauth/request_token}
And here is the cause message from the Throwable I am getting in the onFailure callback from Retrofit:
com.google.gson.stream.MalformedJsonException:
Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
Has anyone got any idea what might cause this?
Finally figured it out, hope this helps someone in future...
The response body from the Twitter API for oauth/request_token isn't encoded as JSON; you will need to read it from the response buffer. Specifically, when implementing the API with Retrofit, you will want your Retrofit interface to return ResponseBody (rather than your custom class), remove GSON from the Retrofit builder and, in the onResponseCallback from Retrofit, write the following code to read the buffer to a string, then split the string on & to get each key val pair, then you can split each of these on = and make sure you have all 3 values before constructing your model:
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
response.body()?.also { body ->
body.source().readString(Charsets.UTF_8).split('&').map { param ->
param.split('=').let { keyVal ->
keyVal[0] to keyVal[1]
}
}.toMap().let { paramMap ->
val oauthToken = paramMap["oauth_token"]
val oauthTokenSecret = paramMap["oauth_token_secret"]
val oauthCallbackConfirmed = paramMap["oauth_callback_confirmed"]?.toBoolean()
if (oauthToken == null || oauthTokenSecret == null || oauthCallbackConfirmed == null) {
onFailure()
} else {
onSuccess(
TwitterRequestToken(
oauthToken,
oauthTokenSecret,
oauthCallbackConfirmed
)
)
}
}
} ?: onFailure()
}
I am developing an Android application using Kotlin. In my application, I am consuming GraphQL API using Apollo Client. What I am trying to do now is that I want to retrieve a response field of the response.
This is my code
protected fun _handleLoginButtonClick(view: View) {
val apolloClient = ApolloClient.builder()
.serverUrl("https://app.herokuapp.com/graphql")
.okHttpClient(OkHttpClient())
.build()
val loginMutation = LoginMutation.builder()
.identity(view.etf_email.text.toString())
.password(view.etf_password.text.toString())
.build()
view.tv_login_error_message.text = "Started making request"
apolloClient.mutate(loginMutation).enqueue(object: ApolloCall.Callback<LoginMutation.Data>() {
override fun onFailure(e: ApolloException) {
view.tv_login_error_message.text = e.message
}
override fun onResponse(response: Response<LoginMutation.Data>) {
//here I dont know how to retrieve a field, accessToken
}
})
}
As you can see the comment in the onResponse callback, I cannot figure out how to retrieve the accessToken field. How can I retrieve it?
OnResponse Contains response Object and it has data object from where you can get your fields.
apolloClient.mutate(loginMutation).enqueue(object: ApolloCall.Callback<LoginMutation.Data>() {
override fun onFailure(e: ApolloException) {
view.tv_login_error_message.text = e.message
}
override fun onResponse(response: Response<LoginMutation.Data>) {
//here you can use response to get your model data like accessToken
response.data.(here you can get data from your model. eg accessToken)
}
})
I am using Retrofit. Using Kotlin. I need to know the resonse status code. Like is it 200 or 500. How can I get it from the response ?
My Api class:
interface Api {
#POST("user/code/check")
fun checkSmsCode(#Body body: CheckCodeBody): Single<Response<Void>> }
This is how I am calling Api. But note that SERVE DOES NOT RETURN CODE FIELD IN RESPONSE BODY!
api.checkSmsCode(
CheckCodeBody(
code = code
)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
//HOW TO CHECK STATUS RESPONSE STATUS CODE HERE???
},
{ e ->
when (e) {
is IOException -> view?.showNoNetworkAlert()
else -> view?.invalidCodeError()
}
}
).also {}
As I understood, in Java it was a easy peasy thing.
You just use response.code() or something similar and that's it. But how to achieve it in Kotlin?
so your on response should look something like this
override fun onResponse(call: Call<MyModel>?, response: Response<MyModel>?) {
//
}
})
then inside that you should just to able to do
override fun onResponse(call: Call<MyModel>?, response: Response<MyModel>?) {
response.code()
}
})
is this what your talking about?
yo need to use it
interface OnlineStoreService{
#Headers("Content-Type: application/json","Connection: close")
#POST
fun getDevices(
#Url url: String,
#Header("Authorization") token: String,
#Body apiParams: APIParams
): Observable<OnlineStoresInfo>
}
.subscribe({ onlineStoresInfo -> // or it -> where "it" it's your object response, in this case is my class OnlineStoresInfo
loading.value = false
devices.value = onlineStoresInfo.devices
}, { throwable ->
Log.e(this.javaClass.simpleName, "Error getDevices ", throwable)
loading.value = false
error.value = context.getString(R.string.error_information_default_html)
})
.subscribe({ it ->
// code
}, { throwable ->
//code
})
If you haven't configure your retrofit request method to return a Response<*> you won't be able to have the response code. Example:
interface SomeApi{
#POST("user/code/check")
fun checkSmsCode(#Body body: CheckCodeBody): Single<Response<String>>
}
And after you finish your request:
.subscribe({
//access response code here like : it.code()
//and you can access the response.body() for your data
//also you can ask if that response.isSuccessful
})