【问题标题】:NetworkOnMainThreadException with URL().readText() in kotlin coroutineskotlin 协程中带有 URL().readText() 的 NetworkOnMainThreadException
【发布时间】:2019-04-01 13:10:26
【问题描述】:

我在 android studio 工作,正在使用 kotlin 协程从 API 检索结果。

我需要等到协程完成,所以我可以从中分配一个全局变量。

我已经测试了网址,没问题。 我尝试了常规线程,它可以工作,但无法让主线程等待它完成。

我尝试使用 Fuel.get() 并且效果很好,但我想使用 URL()。

var response = "";
val req = "url.com"
runBlocking { launch {
            response = URL(req).readText()
} }

谁能告诉我为什么这段代码不起作用?它抛出一个 NetworkOnMainThreadException,但它被包裹在一个协程中。

【问题讨论】:

  • readText() 可能不是suspend 函数。您还没有指出您希望协程使用哪个线程。 runBlocking 将冻结 UI,即使您在后台线程上执行网络 I/O。我建议使用比java.net.URL 更新、更强大的HTTPS 客户端API。
  • 您应该在ViewModel 中使用viewModelScope 进行自动后台调度。

标签: android url kotlin


【解决方案1】:

试试这个方法

var response = "";
val req = "url.com"
runBlocking<Unit> { 
GlobalScope.launch {
    response = URL(req).readText()
}
    //Work with the response here
}

您可以预览所有协程文档here

【讨论】:

  • 我试过了,response 仍然没有在runBlocking 子句中赋值。
  • 您仍然遇到 NetworkOnMainThreadException?还是只是response没有分配?
  • 只是没有分配响应,修复了异常!
  • GlobalScope 推荐在 Android 中使用吗?片段销毁时如何取消此范围。
【解决方案2】:

如果您只想在另一个线程中处理URL(req).readText()的结果,请执行以下代码。

var response = "";
val req = "url.com"
runBlocking<Unit> { 
GlobalScope.launch {
    response = URL(req).readText()
    //here is another thread,handle response here
}
 //here is main thread, you can't get the result of URL(req).readText() because io operation need a long time .

}

如果你在主线程中处理结果,请使用 Hanlder 类

【讨论】:

    【解决方案3】:

    我修好了。最终使用 AsyncTask 从 URL 读取,并使用 Handler 安排处理结果。

    var response = ""
    
    @SuppressLint("StaticFieldLeak")
    inner class Retriever : AsyncTask<String, String, String>() {
          override fun doInBackground(vararg args : String?): String {
                val urlRequest = args[0].toString()
                var urlResponse = "";
    
                //Try to extract url
                try {
                    urlResponse = URL(urlRequest).readText()
                    println("SUCCESS in Retrieve.")
                } catch (e : Exception) {
                    println("EXCEPTION in Retrieve.")
                    e.printStackTrace()
                }
                return urlResponse;
            }
    
            //Assigns value to response
            override fun onPostExecute(result: String?) {
                response = result.toString() //Result possibly void type
        }
    }
    
    override fun onCreate() {
        Retriever().execute("url.com")
        Handler().{/*Handle response here*/, 10000)
    }
    

    【讨论】:

    【解决方案4】:

    对我来说,下面的代码运行顺利。

    var response = "";
    val req = "yoururl.com"
    runBlocking {    
     try {
      withTimeout(5000) {  // 5 seconds for timeout
        launch(Dispatchers.IO) {    // using IO Dispatcher and not the default
         response = URL(req).readText()
        } // launch
      } // timeout
     } catch (e:Exception) {  // Timeout, permission, URL or network error
       response="Error"   // Here one uses a not valid message
     }
     // Here one manages 'response' variable for error handling or valid output.
    

    AndroidManifest.xml中添加权限也很重要,在主manifest标签内:

    <uses-permission android:name="android.permission.INTERNET"/>
    

    这种方法的一个优点(对于不太长的任务)是简单的,因为它不需要使用callback 例程,因为它是一个顺序代码。

    【讨论】:

      猜你喜欢
      • 2021-04-21
      • 2019-04-04
      • 1970-01-01
      • 2019-11-28
      • 2018-05-18
      • 2018-04-13
      • 1970-01-01
      • 2019-04-29
      • 2020-01-04
      相关资源
      最近更新 更多