1
@SuppressLint("SetTextI18n")
fun wordCall() {
    val textView: TextView = findViewById(R.id.blue)
    val client = OkHttpClient()
    val url = URL("https://reqres.in/api/users?page=2")
    val request = Request.Builder()
        .url(url)
        .get()
        .build()
    val response = client.newCall(request).execute()
    val responseBody = response.body!!.string()
    //Response
    textView.text = "Response Body: $responseBody"
}
}

im new in kotlin and struggling to do a network request I'm using okhttp and the error im getting in logcat is java.lang.RuntimeException : Unable to start activity android.os.NetworkOnMainThreadException.

narcis dpr
  • 939
  • 2
  • 12
  • 32

4 Answers4

1

Your Exception actually tells you exactly what you are doing wrong. You are not using another thread to perform NetworkOperations. Instead, you perform the network operation on your UI-Thread, which cannot (does not) work on Android.

Your code that connects to the url should be executed for example inside an AsyncTasks doInBackground() method, off the UI-Thread.

Take a look at this question on how to use the AsyncTask: How to use AsyncTask

Hossein
  • 439
  • 3
  • 9
0

Okhttp is Async thus you should create a new Trhead to show the response:

 client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
    // Handle this
}

override fun onResponse(call: Call, response: Response) {
    // here is the response from the server
}

})

Igmer Rodriguez
  • 164
  • 4
  • 18
0

Yes you are right , according to the documentation -

Android applications normally run entirely on a single thread by default the "UI thread" (or "main thread"). This means anything your application is doing in the UI thread that takes a long time to complete.

Therefore, any method that runs in the UI thread should do as little work as possible on that thread. In particular, activities should do as little as possible to set up in key life-cycle methods such as onCreate() and onResume(). Potentially long running operations such as network or database operations, or computationally expensive calculations such as resizing bitmaps should be done in a worker thread (or in the case of databases operations, via an asynchronous request).

Though you can call wordCall method by giving StrictMode ThreadPolicy permission , but use it for debugging purpose only .

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_create_account)

        val policy = ThreadPolicy.Builder()
            .permitAll().build()
        StrictMode.setThreadPolicy(policy)

        //call method now
    }

I think im on the right track and I believe the error is I need to use runOnUiThread but im not sure how to implement it.

Based on your thinking I am sharing example code , you may check - we will use schedule method of timer to schedule a task , and retrieve the response body and set text of result to a textview through runOnUiThread .

Here it is -

    package com.example.kotlin

import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import okhttp3.OkHttpClient
import okhttp3.Request
import java.lang.Exception
import java.net.URL
import java.util.*
import android.app.Activity
import android.content.Context

class CreateAccount : AppCompatActivity() {

    lateinit var textView: TextView
    var timer = Timer()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_create_account)

       
        timer.schedule(WordCall(this), 0, (30 * 60 * 1000).toLong())


    }



}

class WordCall(val context: Context) : TimerTask() {
    override fun run() {
        val textView: TextView = (context as Activity).findViewById(R.id.blue)
        val client = OkHttpClient()
        val url = URL("https://reqres.in/api/users?page=2")

        val request = Request.Builder()
            .url(url)
            .get()
            .build()

        try {
            val response = client.newCall(request).execute()
            val responseBody = response.body()!!.string()
            (context as Activity).runOnUiThread {
                textView.text = "Response Body: $responseBody"
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}
Zahid Islam
  • 740
  • 1
  • 5
  • 11
0

Your Exception actually tells you what you are doing wrong.

NetworkOnMainThreadException means that you are performing the network operation on your main thread, which actually blocks your main thread. That's why Android doesn't support network calling on the main thread. Rather you can do it on your UI thread

In your code, you are using the execute() method to perform the network operation, which actually blocking your main thread and thus creates the exception.

Pleasantly the OkHttp library provides a callback itself to perform network operation on the UI thread as @Igmer mentioned in his answer.

You just need to replace this client.newCall(request).execute() line with this:

client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
    // Handle this
}

override fun onResponse(call: Call, response: Response) {
    // here is the response from the server
}
})

Also, as you are using Kotlin you can look up to the Kotlin coroutines library as it is much more easier and concise than using a callback.