【问题标题】: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

    【讨论】:

    • AsyncTask 已弃用,最好使用协程
    • 其实我的目的是解释异常的原因。 @IR42
    【解决方案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,因为它比使用回调更容易和简洁。

        【讨论】:

          猜你喜欢
          • 2020-12-06
          • 2018-07-30
          • 2018-02-20
          • 2021-02-25
          • 1970-01-01
          • 1970-01-01
          • 2020-09-19
          • 2020-12-25
          • 2018-05-11
          相关资源
          最近更新 更多