@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"
}
}
我是kotlin的新手,正在努力做一个网络请求,我使用okhttp,我在logcat中得到的错误是java.lang.RuntimeException : Unable to start activity android.os.NetworkOnMainThreadException
。
你的Exception实际上告诉你你做错了什么。您没有使用另一个线程来执行NetworkOperations。相反,你在你的ui线程上执行网络操作,这在Android上不能(不)工作。
连接到url的代码应该在AsyncTasks的doInBackground()方法中执行,而不是在UI-Thread中执行。
看一下这个问题如何使用AsyncTask:如何使用AsyncTask
Okhttp是异步的,因此你应该创建一个新的Trhead来显示响应:
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
}
})
是的,根据文档-
Android应用程序通常在默认情况下完全运行在单个线程上。(或"主线程")。这意味着你的应用程序在UI线程中做的任何事情都需要很长时间才能完成。
因此,任何在UI线程中运行的方法都应该做得尽可能少尽可能在这条线上工作。特别是活动要做尽量少设置在关键的生命周期方法如onCreate()和onResume()。可能长时间运行的操作,例如网络或数据库操作,或计算昂贵像调整位图大小这样的计算应该在worker中完成线程(或者在数据库操作的情况下,通过异步请求)。
虽然你可以通过给StrictMode ThreadPolicy
权限来调用wordCall
方法,但它只用于调试目的。
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
}
我认为我在正确的轨道上,我相信错误是我需要使用runonuthread,但我不知道如何实现它。
根据你的想法,我分享示例代码,你可以检查-我们将使用定时器的schedule
方法来调度任务,并通过runOnUiThread
检索响应体并将结果文本设置为textview。
在这里-
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()
}
}
}
你的Exception实际上告诉你你做错了什么。
NetworkOnMainThreadException意味着您正在主线程上执行网络操作,这实际上阻塞了主线程。这就是Android不支持主线程网络调用的原因。相反,你可以在你的UI线程上完成它
在你的代码中,你正在使用execute()
方法来执行网络操作,这实际上阻塞了你的主线程,从而创建了异常。
愉快的OkHttp库本身提供了一个回调函数来对UI线程执行网络操作。正如@Igmer在他的回答中提到的。
您只需要将client.newCall(request).execute()
行替换为:
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
}
})
同样,当你使用Kotlin时,你可以查阅Kotlin协程库,因为它比使用回调更简单、更简洁。