kittinunf / fuel

The easiest HTTP networking library for Kotlin/Android

Home Page:https://fuel.gitbook.io/documentation/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Synchronous Code throws networkonmainthreadexception

RyanGaudion opened this issue · comments

Bug Report

Description

Trying to return a value after making a Post request. Async code makes the request without issues but I'm unable to access the return value from outside the callback function. Moving the code to Synchronous Code throws the networkonmainthreadexception

To Reproduce

Steps to reproduce the behavior:

Working Async Code:

val url = "http://10.0.2.2:3000/restaurant/create"
val postData = listOf("name" to r.name, "cuisine" to r.cuisine, "address" to r.address, "lat" to r.lat, "lon" to r.lon)

var returnId = 0L
val postAsync = url.httpPost(postData).response { request, response, result ->
    when (result) {
        is Result.Success -> {
            val stringResult = result.get().decodeToString()
            val jsonObject = JSONObject(stringResult)
            val id = jsonObject.getLong("id")
            returnId = id;
            //Toast.makeText(this@MainActivity,"", Toast.LENGTH_LONG).show()
        }

        is Result.Failure -> {
            AlertDialog.Builder(this@MainActivity)
                .setPositiveButton("Okay", null)
                .setMessage("Upload Restaurant to web - Http Error:   ${result.getException().message}")
                .show()
        }
    }
}
postAsync.join()
return returnId

Exception Throwing Synchronous Code

val url = "http://10.0.2.2:3000/restaurant/create"
val postData = listOf("name" to r.name, "cuisine" to r.cuisine, "address" to r.address, "lat" to r.lat, "lon" to r.lon)

var returnId = 0L

val (request, response, result) = url.httpPost(postData).response()

when (result) {
    is Result.Success -> {
        val stringResult = result.get().decodeToString()
        val jsonObject = JSONObject(stringResult)
        val id = jsonObject.getLong("id")
        returnId = id;
    }

    is Result.Failure -> {
        AlertDialog.Builder(this@MainActivity)
            .setPositiveButton("Okay", null)
            .setMessage("Upload Restaurant to web - Http Error:   ${result.getException().message}")
            .show()
    }
}

return returnId

Expected behavior

I need a way to return returnId from this method - waiting for the callback to complete. Currently the first example returns before the callback is complete and the 2nd example throws a networkonmainthreadexception

Hey, thanks for opening/reporting an issue here.

I assume that you are working on Android which is why you see the NetworkOnMainThreadException, as a good programming citizen, you should try to aim for running code asynchronously.

For your specific example, you should find a class that out-lives the network call, on Android this can be Activity, Fragment, ViewModel, or other components. Then, you call Fuel with the asynchronous call, then wait until the network is finished and retrieve the data that you want in the response block.

You couldn't return the returnId like that because the asynchronous HTTP call isn't blocking which is a good thing.

@kittinunf thank you for your reply. Is there any way to manually force it to wait. I know that isn't best practice but for my use case I need this Id as straight afterward I'm adding to a DB (where this Id is required)

Unfortunately no. I think you should embrace the asynchronous nature of this. Android doesn't want you to do networking on mainthread which is a good thing. You should delay your work and do it in the lambda that you pass in.

Anything that I could help further please feel free to let me know, otherwise, I am closing this. ^^ Thanks!