【问题标题】:Kotlin HTTP RequestKotlin HTTP 请求
【发布时间】:2021-11-03 17:34:47
【问题描述】:
@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。
【问题讨论】:
标签:
android
kotlin
okhttp
【解决方案1】:
你的异常实际上告诉你你做错了什么。您没有使用另一个线程来执行 NetworkOperations。相反,您在 UI 线程上执行网络操作,这在 Android 上无法(不)工作。
连接到 url 的代码应该在例如 AsyncTasks doInBackground() 方法中执行,远离 UI 线程。
看看这个关于如何使用 AsyncTask 的问题:How to use AsyncTask
【解决方案2】:
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
}
})
【解决方案3】:
是的,根据文档,你是对的 -
默认情况下,Android 应用程序通常完全在单个线程上运行,即“UI 线程”(或“主线程”)。这意味着您的应用程序在 UI 线程中所做的任何事情都需要很长时间才能完成。
因此,在 UI 线程中运行的任何方法都应该尽可能少地执行
尽可能在该线程上工作。特别是活动应该做
尽可能少地在关键生命周期方法中设置,例如
onCreate() 和 onResume()。可能需要长时间运行的操作,例如
网络或数据库操作,或计算成本高
调整位图大小等计算应在工作人员中完成
线程(或者在数据库操作的情况下,通过异步
请求)。
虽然您可以通过授予 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
}
我认为我在正确的轨道上,我相信错误是我需要使用 runOnUiThread 但我不确定如何实现它。
根据你的想法,我正在分享示例代码,你可以检查 - 我们将使用定时器的schedule方法来安排任务,并通过runOnUiThread检索响应正文并将结果文本设置为文本视图。
在这里-
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()
}
}
}
【解决方案4】:
你的异常实际上告诉你你做错了什么。
NetworkOnMainThreadException 表示您正在主线程上执行网络操作,这实际上阻塞了您的主线程。这就是为什么 Android 不支持在主线程上进行网络调用的原因。相反,您可以在 UI 线程
上执行此操作
在您的代码中,您使用execute() 方法执行网络操作,这实际上阻塞了您的主线程并因此创建了异常。
正如@Igmer 在他的回答中提到的那样,OkHttp 库本身提供了一个回调来在 UI 线程 上执行网络操作。
您只需将 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 coroutines library,因为它比使用回调更容易和简洁。