I'm creating an Android app. When using the simulator (Android v10) the callback of my OkHttp Get request is not called. (Same with Retrofit).
When I test on the real device it works fine.
Here is my code:
val url = "https://my.url/somewhere"
var client = OkHttpClient()
var request = Request.Builder().url(url).build()
client.newCall(request).enqueue( object: Callback {
override fun onFailure(call: Call, e: IOException) {
showErrorDialog()
}
override fun onResponse(call: Call, response: Response) {
debug("Got response")
moveToMainView(Gson().fromJson(response.body?.string(), Model.Data::class.java))
}
})
Any ideas why it would not work on the Simulator?
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.
My Android Application is base on a TCP protocol.
When I'm initializing a connection to the server, I'm sending a special bytes message and have to wait the response of the server.
In all the repositories example I have seen the repository have always methods to call the source of information with a return (from Android Developers) :
class UserRepository {
private val webservice: Webservice = TODO()
// ...
fun getUser(userId: String): LiveData<User> {
// This isn't an optimal implementation. We'll fix it later.
val data = MutableLiveData<User>()
webservice.getUser(userId).enqueue(object : Callback<User> {
override fun onResponse(call: Call<User>, response: Response<User>) {
data.value = response.body()
}
// Error case is left out for brevity.
override fun onFailure(call: Call<User>, t: Throwable) {
TODO()
}
})
return data
}
}
The function getUser return a data of LiveData.
In my app the method Login return nothing because I wait for the server to send bytes with a special code to know that is responding to my login request.
Is there a way to implement this pattern with TCP protocols like that ?
Thanks
They honestly should have just written the following code:
class UserRepository {
private val webService: WebService = TODO()
// ...
fun getUser(userId: String, successCallback: (User) -> Unit) {
webService.getUser(userId).enqueue(object : Callback<User> {
override fun onResponse(call: Call<User>, response: Response<User>) {
successCallback(response.body())
}
// Error case is left out for brevity.
override fun onFailure(call: Call<User>, t: Throwable) {
}
})
}
}
LiveData is not meant to represent one-off callbacks.
Then call it as
userRepository.getUser(userId) { user ->
// do whatever
}
For a proper reactive implementation, refer to https://stackoverflow.com/a/59109512/2413303
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/
I need to make a post request to an endpoint from my android project. I am using Kotlin. Will I need to create a separate OKHttpClient Class for this.
Here's how you can achieve this:
val payload = "test payload"
val okHttpClient = OkHttpClient()
val requestBody = payload.toRequestBody()
val request = Request.Builder()
.method("POST", requestBody)
.url("url")
.build()
okHttpClient.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// Handle this
}
override fun onResponse(call: Call, response: Response) {
// Handle this
}
})
Don't forget to import:
import okhttp3.RequestBody.Companion.toRequestBody
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.