My response with Retrofit2 and RxJava dont work corretilly - android

I am making a call in an api with Retrofit and the response take 8000 ms to bring a body, and i trying add it with postValue in an MutableLiveData in onNext, but its dont work
#POST("Account/Login")
fun doLogin(
#Body userLogin: UserLogin
): Observable<Response<UserLogged>>
This is my call to api
responseObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
userLogged.postValue(it.body())
resultCode.postValue(it.code())
},
{
message = it.message
}
)
And where i try add body in a userLogged(MutableLiveData)
I trying add true in MutableLiveData in onComplete but its dont work, i expected that body add to userLogged MutableLiveData when end call(8000ms)

Related

Why do I get " I/system_server: oneway function results will be dropped but finished with status OK and parcel size 4 " message?

I am writing an android application using Kotlin ,Retrofit ,OKHttp and Rxjava when make a call to my API in order to add a new user to database i get the following message in logcat:
I/system_server: oneway function results will be dropped but finished with status OK and parcel size 4
The problem is that nothing happens no user is added in fact the API does not receive any requests from my app even thought i have the appropriate permissions and a android:usesCleartextTraffic="true" annotation in my AndroidManifest.xml.
By putting println() in various places in the code I was able to confirm that the function responsible for making the call is in fact executed but code responsible for handling response is never called and no error is reported.
Here is the code of above mentioned function:
private fun addUser(user: UserDataObject) {
val client = OkHttpClient.Builder()
.addInterceptor(BasicAuthInterceptor("admin", "admin")) // temporarily hardcoded
.build()
val requestInterface = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.client(client)
.build().create(GetData::class.java)
myCompositeDisposable?.add(
requestInterface.addUser(user)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponse, this::onError)
)
}
and the code of the interface:
interface GetData {
#POST("/api/some/API/URL")
fun addUser(#Body user: UserDataClass) : Completable}
also ( just in case ) the code of interceptor:
class BasicAuthInterceptor(username: String, password: String): Interceptor {
private var credentials: String = Credentials.basic(username, password)
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
var request = chain.request()
request = request.newBuilder().header("Authorization", credentials).build()
return chain.proceed(request)
}
}
Can you please tell me why is my app not able to communicate with API ?
While searching for answer on the internet i found this question so I gather that it has something to do with selinux permissions but this doesn't help me at all since I'm just a beginner.

Http requests with OkHttp Interceptor doesn't works

I'm using Retrofit with OkHttp Interceptor to work with API.
Interceptor adding cookie header to each request.
Interceptors code:
class AddCookiesInterceptor: Interceptor {
#Inject
lateinit var cookiesDao: CookiesDao
init {
App.getAppComponent().inject(this)
}
#SuppressLint("CheckResult")
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().newBuilder()
cookiesDao.getAll()
.subscribeOn(Schedulers.io())
.subscribe { cookies ->
builder.addHeader("Cookie", "JWT=" + cookies.jwt)
}
return chain.proceed(builder.build())
}
}
While debuging i see, that interceptor updates request and adds cookie header with value, but when server reachs the request it returns an error (400 http code auth again).
if i manualy add Header into request like this
#GET("/api.tree/get_element/")
#Headers("Content-type: application/json", "X-requested-with: XMLHttpRequest", "Cookie: jwt_value")
fun getElementId(): Maybe<ResponseBody>
Api returns 200 http code and it works.
Your code is not working because you are adding the header asynchronously, this is a "timeline" of what's happening in your flow:
init builder -> ask for cookies -> proceed with chain -> receive cookies dao callback -> add header to builder which has been already used
What you need to do is retrieve the cookies synchronously, to accomplish this you can use the BlockingObseervable and get something like this.
Using a synchronous function won't cause any trouble since the interceptor is already running on a background thread.
#SuppressLint("CheckResult")
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().newBuilder()
val cookies = cookiesDao.getAll().toBlocking().first()
builder.addHeader("Cookie", "JWT=" + cookies.jwt)
return chain.proceed(builder.build())
}

Twitter oauth/request_token 200 code with empty response body

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()
}

How to make synchronous call using Retrofit

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)

Retrieve JSON response from rxjava/retrofit POST request

I've created a POST request using rxjava and retrofit that successfully hits my backend server and logs the user in (I get a 201 response in my console, all good). However, I want to then retrieve the users access token that is returned, but when I try to access the rxjava result, it just gives me the object I passed to it. Nowhere can I find out how to get the json success response. I have also verified there is in fact a response in Postman, so it's something with how I make this call.
Here is my Retrofit portion
#Headers("Content-Type: application/json")
#POST("api/v1/login")
fun loginTask(#Body credentials: UserLogin)
: Observable<UserLogin>
And the correspoinding API function
class ApiFunctions(val apiService: LunchVoteApi) {
fun provideHello(): io.reactivex.Observable<Hello> {
return apiService.helloMessage()
}
fun loginTask(email: String, password: String): io.reactivex.Observable<UserLogin> {
val credentials: UserLogin = UserLogin(email, password)
return apiService.loginTask(credentials)
}
}
The UserLogin model that is deserialized by Gson
data class UserLogin(
#SerializedName("email") val email: String,
#SerializedName("password") val password: String
)
And finally the call in my LoginActivity
val loginTask = ApiProvider.provideLoginTask()
override fun doInBackground(vararg params: Void): Boolean? {
// TODO: attempt authentication against a network service.
try {
// Simulate network access.
// Thread.sleep(2000)
compositeDisposable.add(
loginTask.loginTask(mEmail, mPassword)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe ({
result ->
System.out.println(result.toString())
}, { error ->
System.out.println(error)
})
)
} catch (e: InterruptedException) {
return false
}
The confusion comes when I try to access the result -> portion of the compositeDisposable call. It just prints out the UserLogin object. Am I missing something here? Thanks.
Turns out I was returning my UserLogin type instead of a pojo object with an access token property.
Changing my retrofit call to #Headers("Content-Type: application/json")
#POST("api/v1/login")
fun loginTask(#Body credentials: UserLogin)
: Observable<AccessToken>
And creating a new model
data class AccessToken(
#SerializedName("accessToken") val email: String
)
I am now able to print out the token. Thanks to #john-oreilly

Categories

Resources