简体   繁体   English

使用协程时出现 NetworkOnMainThreadException

[英]NetworkOnMainThreadException while using coroutines

I've been trying to validate the network connection status when a button is pressed.我一直在尝试在按下按钮时验证网络连接状态。 Hereby the code特此代码

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

    findViewById<Button>(R.id.button).setOnClickListener {
        findViewById<TextView>(R.id.logText).setText("Button Pressed"+counter++)
        GlobalScope.launch (Dispatchers.Main) {
            val result  = withContext(Dispatchers.Default){
                isInternetAvailable()
            }
            println(result.toString())
        }
    }
}

suspend fun isInternetAvailable():Boolean {
    return withContext(Dispatchers.Main) {
        try {
            val ipAddr = InetAddress.getByName("google.com")
            //You can replace it with your name
            println("1$ipAddr")
            return@withContext !ipAddr.equals("")
        } catch (e: Exception) {
            println("within exception$e")
            return@withContext false;
        }

    }
}

However, when the button is pressed following output can be seen in the console.但是,当按下按钮时,可以在控制台中看到以下输出。

I/System.out: button clicked
I/System.out: within exceptionandroid.os.NetworkOnMainThreadException
I/System.out: false

Can someone please explain the reason for NetworkOnMainThread Exception?有人可以解释一下 NetworkOnMainThread 异常的原因吗?

Thanks a lot.非常感谢。

withContext(Dispatchers.Main) means to run the code in the lambda on the Main thread, which is not allowed for blocking code. withContext(Dispatchers.Main)表示在主线程上运行 lambda 中的代码,不允许阻塞代码。 For network requests, you should use Dispatchers.IO .对于网络请求,您应该使用Dispatchers.IO

Also, don't use GlobalScope for this, because it will leak your Activity.另外,不要为此使用 GlobalScope,因为它会泄漏您的 Activity。 Use lifecycleScope .使用lifecycleScope lifecycleScope uses the main thread by default, so when you use it, you don't have to specify a dispatcher unless you are making a blocking call. lifecycleScope默认使用主线程,所以当你使用它时,你不必指定调度程序,除非你进行阻塞调用。

And it is convention to make suspend functions switch to the appropriate dispatcher whenever doing blocking calls so you can safely call them from any dispatcher.并且惯例是在进行阻塞调用时让挂起函数切换到适当的调度程序,以便您可以安全地从任何调度程序调用它们。 This means your suspend functions are not blocking calls, and can be called from coroutines without having to wrap them in withContext .这意味着您的挂起函数不会阻塞调用,并且可以从协程调用,而无需将它们包装在withContext

So with all of the above, your code becomes:因此,有了以上所有内容,您的代码将变为:

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

    findViewById<Button>(R.id.button).setOnClickListener {
        findViewById<TextView>(R.id.logText).setText("Button Pressed"+counter++)
        lifecycleScope.launch {
            val result = isInternetAvailable()
            println(result.toString())
        }
    }
}

suspend fun isInternetAvailable(): Boolean {
    return withContext(Dispatchers.IO){
        try {
            val ipAddr = InetAddress.getByName("google.com")
            //You can replace it with your name
            println("1$ipAddr")
            return@withContext !ipAddr.equals("")
        } catch (e: Exception) {
            println("within exception$e")
            return@withContext false;
        }

    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM