This is my code, I'm getting success code 200 but I need to wait until the final response is fetched.
val retroService =
RetroInstance().getRetroInstance("$url/")
.create(ApiService::class.java)
val call = retroService.getResponse(
"Token *****"
)
call.enqueue(object : Callback<MyResponse> {
override fun onResponse(
call: Call<MyResponse>,
response: Response<MyResponse>
) {
if (response.isSuccessful) {
//After the first response it stopped. How to wait until complete response is fetched?
} else {
response.errorBody()
}
}
override fun onFailure(call: Call<MyResponse>, t: Throwable) {
}
})
}
I have to wait until the final response is fetched. I tried it in postman the final output is displayed.
Related
I am working on an IoT project where I have an API that needs two HTTP requests to fetch a value (req1 to measure a distance, req2 to read the measured distance). I am trying to communicate with the APIs using Kotlin and OkHttp3, in the following manner:
1- Send req1 then req2
2- Make req2 return the measurement result to the main thread for further actions
However, sometimes the req2 seems to finish before req1, making the results unstable, also, req2 returns before the HTTP request finishes.
Req1 Function:
fun req1() {
val request = Request.Builder()
.url("https://IoT-API.com/Write")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: okhttp3.Call, e: IOException) {
e.printStackTrace()
}
override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response")
}
}
})
}
Req2 Function:
fun req2(): String {
var distance = ""
val request = Request.Builder()
.url("https://IoT-API.com/Read")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: okhttp3.Call, e: IOException) {
e.printStackTrace()
}
override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response")
val resp = response.peekBody(Long.MAX_VALUE).string()
val jsonObject = JSONTokener(resp).nextValue() as JSONObject
distance = jsonObject.getString("value")
}
}
})
return distance
}
Calling the functions:
checkButton.setOnClickListener {
req1()
var distance = req2()
resultTextView.text = "Distance: $distance"
}
I am aware that the cause of this issue is that HTTP requests are being performed on background threads, however, I am not sure how to solve this.
When I use retrofit request to get value from web api, then assign it to liveData. But it always return null before it set value to responseLiveData in onResponse.
fun fetchContents(): LiveData<String> {
val responseLiveData: MutableLiveData<String> = MutableLiveData()
val flickrRequest: Call<String> = flickrApi.fetchContents()
flickrRequest.enqueue(object : Callback<String> {
override fun onFailure(call: Call<String>, t: Throwable) {
Log.e(TAG, "Failed to fetch photos", t)
}
override fun onResponse(
call: Call<String>,
response: Response<String>
) {
Log.d(TAG, "Response received")
responseLiveData.value = response.body()
}
})
return responseLiveData
}
// then assign to the liveData in viewModel
val flickrLiveData: LiveData<String> = FlickrFetchr().fetchContents()
This code from the big nerd ranch guide, I find it can not work outOfBox, so I issue this error.
I tried to use retrofit to get the response data of web api, but the result of response data seems not sync as the same.
fun fetchData(): LiveData<String> {
val auth = Credentials.basic(name, pass)
val request: Call<JsonElement> = webApi.fetchData()
val response: MutableLiveData<String> = MutableLiveData()
request.enqueue(object : Callback<JsonElement> {
override fun onFailure(call: Call<JsonElement>, t: Throwable) {
Log.e(TAG, "Failed to fetch token", t)
}
override fun onResponse(call: Call<JsonElement>, response: Response<JsonElement>) {
response.value = response.body()
Log.d(TAG, "response: ${response.value}") // I can get the result of response
}
})
return response // But the function return with the null
}
You might need handler.
The enqueue method doesn´t wait to the response so is normal the null result in your return response.
To solve this, you doesn´t need to return nothing, only put your livedata in the scope class and update the value:
class YourClass {
private var responseMutableLiveData: MutableLiveData<String> = MutableLiveData()
val responseLiveData: LiveData<String>
get() = responseMutableLiveData
fun fetchData() {
webApi.fetchData().enqueue(object : Callback<JsonElement> {
override fun onFailure(call: Call<JsonElement>, t: Throwable) {
Log.e(TAG, "Failed to fetch token", t)
}
override fun onResponse(call: Call<JsonElement>, response: Response<JsonElement>) {
responseMutableLiveData.postValue(response.body())
Log.d(TAG, "response: ${response.value}")
}
})
}
}
The livedata is observed and, when the value changes, then the other class reacts to it.
I need to send the first POST request to the server, insert the received response into the second POST request and send it back to the server. How can I implement this with RxJava? At the moment, everything works, but the second question does not have time to get an answer from the first request and the imageMediaId field is sent empty.
My function:
fun uploadNewImageAndContact(toPath: String?, newContactApi: ContactsApi) {
val file = File(toPath)
val fileReqBody: RequestBody = file.asRequestBody("image/*".toMediaTypeOrNull())
val part: MultipartBody.Part =
MultipartBody.Part.createFormData("upload", file.name, fileReqBody)
// First Request
imagesService.postImage(part).enqueue(object : Callback<ResponseBody> {
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
}
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.code() == 201) {
// Get Image response Id
val gson = Gson()
val imageResponse =
gson.fromJson(response.body()?.string(), ImageResponse::class.java)
imageMediaId = imageResponse.ids[0]
// Second Request
contactsService.postNewContact(newContactApi)
.enqueue(object : Callback<ContactsApi> {
override fun onFailure(call: Call<ContactsApi>, t: Throwable) {
}
override fun onResponse(call: Call<ContactsApi>, response: Response<ContactsApi>) {
}
})
}
}
})
}
My ViewModel function:
val newContactsApi = ContactsApi(id = "", firstName = mFirstName, lastName = mLastName, phone = mPhone,
email = mEmail, notes = mNotes, images = "https://mydb.site.io/media/${apiRepository.imageMediaId}")
apiRepository.uploadNewImageAndContact(toPath, newContactsApi)
My GET request doesn't work properly in Android Studio. I tried to put the same requests in Postman and in result I always got response code 200 with correct body. However, using Retrofit I get onResponse (200 code) when response body is empty (there is no data) and onFailure when response body is not empty (there are some reservations).
EDIT: I have just noticed such error in logs: Expected BEGIN_OBJECT but was STRING at line 1 column 22 path $[0].reservationDate
EDIT2: JSON RESPONSE
[
{
"reservationDate": "2019-10-30",
"id": "4"
}
]
API Service:
#Headers("Authorization: Bearer ...")
#GET("reservations")
fun getSchedule(#Query("id") id: Int,
#Query("reservationDate") reservationDate: LocalDate
): Call<List<ScheduleModel>>
companion object {
fun create(): ScheduleService{
var retrofit = Retrofit.Builder()
.baseUrl("myUrl")
.addConverterFactory(GsonConverterFactory.create())
.build()
return retrofit.create(ScheduleService::class.java)
}
}
}
data class ScheduleModel(
val id: Int,
val reservationDate: LocalDate
)
fun getReservations() {
var service = ScheduleService.create()
var localdate = LocalDate.of(2019,10,30)
var call = service.getSchedule(4, localdate)
call.enqueue(object : Callback<List<ScheduleModel>> {
override fun onResponse(call: Call<List<ScheduleModel>>, response: Response<List<ScheduleModel>>) {
if (response.code() == 200) {
Toast.makeText(applicationContext, "It's ok", Toast.LENGTH_SHORT).show()
}
}
override fun onFailure(call: Call<List<ScheduleModel>>, t: Throwable) {
Toast.makeText(applicationContext, "Failure", Toast.LENGTH_SHORT).show()
}
})
}
Your id and reservationDate in response is a String but you try to parse it as Int and LocalDate. Change the types and it's gonna be work.