Using OkHttp3 to send requests in a specific order - android

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.

Related

How to wait until final response is fetched in retrofit GET request

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.

calling response value inside onCreate (okhttpclient)

There is a GET operation above the onCreate code. I want to get the response value of this get operation into onCreate.
My Code
fun run() {
val request = Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build()
}
}
})
}
}
It's simple with Kotlin coroutines. Use can use suspendCoroutine to work with callback, and lifecycleScope in Activity to launch a coroutine. The code will be something like the following:
suspend fun run(): String = suspendCoroutine { continuation ->
val request = Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
continuation.resumeWithException(e) // resume calling coroutine
e.printStackTrace()
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response")
for ((name, value) in response.headers) {
println("$name: $value")
}
println(response.body!!.string())
val qrq = response.body!!.string()
continuation.resume(qrq) // resume calling coroutine
}
}
})
}
And call the method run in the coroutine, launched in the onCreate method:
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme_MainActivity)
super.onCreate(savedInstanceState)
lifecycleScope.launch {
val qrq = run()
// use qrq, for example to update UI
}
//another code ......
}

How to get result from response inside method?

I am new android developer, how can I get result form this snippet, what way does exist, because it doesn't return anything, because of I'm adding element inside onResponse, but using only kotlin module:
private fun foo(list: ArrayList<CurrencyModel> = ArrayList()): ArrayList<CurrencyModel> {
val request = Request.Builder().url(BASE_URL_YESTERDAY).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
val body = response.body?.string()
val data = Gson().fromJson(body, Currencies::class.java)
list.add(CurrencyModel("USD", data.rates.USD, 0.0))
list.add(CurrencyModel("SEK", data.rates.SEK, 0.0))
list.add(CurrencyModel("EUR", data.rates.EUR, 0.0))
}
})
return list
}
}
You can give your function a callback parameter that's called when the response is receieved. And you shouldn't have an input list in this case, because if you have multiple sources modifying it at unpredictable future moments, it will be difficult to track.
The function can look like this:
private fun getCurrencyModels(callback: (ArrayList<CurrencyModel>) {
val request = Request.Builder().url(BASE_URL_YESTERDAY).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
val body = response.body?.string()
val data = Gson().fromJson(body, Currencies::class.java)
val list = arrayListOf(
CurrencyModel("USD", data.rates.USD, 0.0)),
CurrencyModel("SEK", data.rates.SEK, 0.0)),
CurrencyModel("EUR", data.rates.EUR, 0.0))
)
callback(list)
}
})
}
And then to use it:
getCurrencyModels { modelsList ->
// do something with modelsList when it arrives
}
An alternative is to use coroutines, which allow you to do asynchronous actions without callbacks. Someone has already created a library that lets you use OkHttp requests in coroutines here. You could write your function as a suspend function like this:
private suspend fun getCurrencyModels(): ArrayList<CurrencyModel> {
val request = Request.Builder().url(BASE_URL_YESTERDAY).build()
val client = OkHttpClient()
val response = client.newCall(request).await()
val body = response.body?.string()
val data = Gson().fromJson(body, Currencies::class.java)
return arrayListOf(
CurrencyModel("USD", data.rates.USD, 0.0)),
CurrencyModel("SEK", data.rates.SEK, 0.0)),
CurrencyModel("EUR", data.rates.EUR, 0.0))
)
}
and then use it like this:
lifecycleScope.launch {
try {
val currencyModels = getCurrencyModels()
// do something with currencyModels
} catch (e: IOException) {
// request failed
}
}
Coroutines make it really easy to avoid leaking memory when your asynchronous calls outlive your Activity or Fragment. In this case, if your Activity closes while the request is going, it will be cancelled automatically and references to your Activity will be removed so the garbage collector can release your Activity.
The onResponse() function is only called when the HTTP response is successfully returned by the remote server. Since this response doesn't happen immediately, you can't use the result in your code immediately. What you could do is use a ViewModel and LiveData variable and add the values to that variable in onResponse(). Something like:
private fun foo(list: ArrayList<CurrencyModel> = ArrayList()) {
val request = Request.Builder().url(BASE_URL_YESTERDAY).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
val body = response.body?.string()
val data = Gson().fromJson(body, Currencies::class.java)
val list: ArrayList<CurrencyModel> = arrayListOf()
list.add(CurrencyModel("USD", data.rates.USD, 0.0))
list.add(CurrencyModel("SEK", data.rates.SEK, 0.0))
list.add(CurrencyModel("EUR", data.rates.EUR, 0.0))
viewModel.list.postValue(list)
}
})
}

How do I successfully make an okHttp request in Kotlin

Issues:
1. The request is not hitting my python flask server.
2. My app crashes whenever this code is run.
*Note: I have used Postman to test the localhost address, which is successful
I am trying to create an okHttp request in my kotlin android application.
I have implemented the needed dependencies in my build.gradle file:
implementation 'com.squareup.okhttp3:okhttp:3.10.0
edit: I included the Internet permission in the manifest file
<uses-permission android:name="android.permission.INTERNET" />
I have tried using my IP as part of the URL, along with a few different localhost links and have determined that the URL is not the issue.
If I am not mistaken, the app crash error occurs during build (), but I cannot wrap my mind around why.
Ideally, this code would hit the rest API and return data (printed as text)
on my login screen.
After a couple of days of researching and trying other request methods, I am unable to find a solution.
val mTextviewResult: TextView = findViewById(R.id.text_view_result)
//http client
var client = OkHttpClient()
val url = "http://localhost:5000/test"
val request = Request.Builder()
.url(url)
.build()
Log.d(request.toString(), ": I build Request")
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.d(url, ": I fail")
e.printStackTrace()
}
#Throws(IOException::class)
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful()) {
Log.d(url, ": I success")
val myResponse = response.body()?.string()
this#LoginActivity.runOnUiThread(object : Runnable {
override fun run() {
mTextviewResult.setText(myResponse)
}
})
}
}
})
To access localhost you should use local IPv4 + server PORT for localhost.
How to get this IPv4:
https://www.whatismybrowser.com/detect/what-is-my-local-ip-address
KOTLIN async request:
private val client = OkHttpClient()
fun run() {
val request = Request.Builder()
.url("http://IPv4_FROM_THE_LINK_ABOVE:5000/test")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
e.printStackTrace()
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response")
for ((name, value) in response.headers) {
println("$name: $value")
}
println(response.body!!.string())
}
}
})
}
Install OkHttp: https://square.github.io/okhttp/

Waiting for thread to complete OkHttp Call

I have been having problems with OkHttp when I nest an OkHttp call inside another OkHttp call I am having a problem with the concurrency. I want to wait for my inner call to finish its thread's job before proceeding. Please take a look.
Note: I am a novice with Kotlin and Multi-thread handling.
private fun parseJson(url: String) {
val request = Request.Builder()
.url(url)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response?) {
var bodyOfProducts = response?.body()?.string()
var collectionJsonObject = jsonParseTool.fromJson(bodyOfProducts, Products::class.java)
val productsWithDetails = ArrayList<ProductDetails>()
for(product in collectionJsonObject.collects){
var concatProductUrl = "https://shopicruit.myshopify.com/admin/products.json?ids=" + product.product_id+ "&page=1&access_token=c32313df0d0ef512ca64d5b336a0d7c6"
val newRequest = Request.Builder()
.url(concatProductUrl)
.build()
val job = thread {
client.newCall(newRequest).enqueue(object : Callback {
override fun onResponse(call: Call, newResponse: Response?) {
var bodyOfProductDetails = newResponse?.body()?.string()
var productJsonObject = jsonParseTool.fromJson(bodyOfProductDetails, ProductDetails::class.java)
productsWithDetails.add(productJsonObject)
}
override fun onFailure(call: Call, e: IOException) {
println("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE")
}
})
}
job.start()
job.join() // This should force my thread to finish before the rest of the code is executed on the main thread.
}
// println(collectionJsonObject.collects[0].product_id)
/*runOnUiThread {
recyclerViewCustomCollections.adapter = CollectionsAdapter(jsonObject)
}*/
}
override fun onFailure(call: Call, e: IOException) {
println("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE")
}
})
}
In this case you should be using execute as mentioned and since http calls are handled asynchronously your thread is redundant and should be removed.
If you want to run code after all the requests are finished one way of doing this is by passing in a onComplete callback function and count the number of requests completed, when all of the threads are completed call the callback function containing the code that should be run after all of the requests.

Categories

Resources