Quessts
Quessts

Reputation: 440

How can I create an asynchronous post request in Kotlin?

I'm trying to send a POST request to a server and get some data. I tried running the request on the main thread but it kept giving me an error so I decided to use coroutines to run it as an async thread. This is what I have so far:

class requests{
    private val client = OkHttpClient()
    
    fun run() = runBlocking {
        launch {
            val postBody = """
        |Releases
        |--------
        |
        | * _1.0_ May 6, 2013
        | * _1.1_ June 15, 2013
        | * _1.2_ August 11, 2013
        |""".trimMargin()

            val request = Request.Builder()
                .url("https://api.github.com/markdown/raw")
                .post(postBody.toRequestBody(MEDIA_TYPE_MARKDOWN))
                .build()

            client.newCall(request).execute().use { response ->
                if (!response.isSuccessful) throw IOException("Unexpected code $response")

                println(response.body!!.string())
            }
        }
    }

    companion object {
        val MEDIA_TYPE_MARKDOWN = "text/x-markdown; charset=utf-8".toMediaType()
    }


}

When I run requests().run() I get the following error:

android.os.NetworkOnMainThreadException

I'm not sure why I'm getting this error, I thought that coroutines creates a new thread to prevent this from happening.

Upvotes: 2

Views: 2351

Answers (1)

Phil Dukhov
Phil Dukhov

Reputation: 88022

runBlocking doesn't switch a thread, so if you call it on a main thread, coroutine will be too launched on a main thread. This method blocks current thread so never use it it the production: blocking a thread is very low performant, you should create a new coroutine instead and launch your code there

Coroutine by itself can work on main thread without problems, but the code you're running can check current thread, and what happens in this case - it throws an exception

The following code should work for you:

class requests{
    private val client = OkHttpClient()
    private val coroutineScope = CoroutineScope(Dispatchers.Default)

    fun run()  {
        coroutineScope.launch {
            val postBody = """
        |Releases
        |--------
        |
        | * _1.0_ May 6, 2013
        | * _1.1_ June 15, 2013
        | * _1.2_ August 11, 2013
        |""".trimMargin()

            val request = Request.Builder()
                .url("https://api.github.com/markdown/raw")
                .post(postBody.toRequestBody(MEDIA_TYPE_MARKDOWN))
                .build()

            client.newCall(request).execute().use { response ->
                if (!response.isSuccessful) throw IOException("Unexpected code $response")

                println(response.body!!.string())
            }
        }
    }

    companion object {
        val MEDIA_TYPE_MARKDOWN = "text/x-markdown; charset=utf-8".toMediaType()
    }


}

An other approach is making run a suspend function and running it from an outside coroutine - it'll be preferred in case you'd like to return a result from your calls

Upvotes: 2

Related Questions