Legendary
Legendary

Reputation: 21

Kotlin OkHTTP android.os.NetworkOnMainThreadException

I am sending http request with kotlin and getting this error

Error: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.xxx/xxx.android.ui.xxx}: android.os.NetworkOnMainThreadException

Additional info: GET request needs to work first. Because the process proceeds according to the response from the URL.

My Code:

override fun onCreate(savedInstanceState: Bundle?) {

    setTheme(R.style.AppTheme_MainActivity)
    super.onCreate(savedInstanceState)

    val request = Request.Builder()
        .url("https://publicobject.com/helloworld.txt")
        .build()

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

    
    }

  
}

Upvotes: 2

Views: 2692

Answers (1)

Sergio
Sergio

Reputation: 30625

Android main components, like Activities, BroadcastReceiver etc, are running in the Main Thread. You can't make a network request (or another potentially long running operation) in the Main Thread. You need to schedule it in the background(worker) thread. By using Call.execute() method you synchronously send the request. i.e. in the Main Thread. You can use method Call.enqueue() to asynchronously send the request:

client.newCall(request).enqueue(object : Callback<Response> {
    override fun onResponse(call: Call<Response>, response: Response<Response>
    ) {
        // handle response
    }

    override fun onFailure(call: Call<ApiResponse>, t: Throwable) {
       // handle error
    }

})

There is also another approach of making and handling the request, using Kotlin Coroutines:

// suspendCoroutine - suspends a coroutine until request is executed
suspend fun makeRequest(): String = suspendCoroutine { continuation ->
    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 {
                continuation.resume(response.body!!.string()) // resume calling coroutine
            }
        }
    })
}

override fun onCreate(savedInstanceState: Bundle?) {

    // ...
    
    lifecycle.coroutineScope.launch { // launching a coroutine
        val result = makeRequest()
        // do something with result
        // save to Preferences
        getPreferences(MODE_PRIVATE).edit().putString("val001", result).apply(); //save prefences
        println("Request complete")
    }
  
}

Upvotes: 3

Related Questions