我目前正在尝试更多地利用kotlin协程。但我面临一个问题:当在这些协程中使用moshi或okhttp时,我得到一个警告:

“不恰当的阻塞方法调用”

解决这些问题的最好方法是什么?我真的不想不合适;-)


当前回答

使用withContext将“不适当的阻塞方法调用”代码包装到另一个上下文中。

也就是说(例如):

如果你正在执行一个读/写阻塞方法调用:

val objects = withContext(Dispatchers.IO) { dao.getAll() }

如果您正在执行阻塞网络请求(使用Retrofit):

val response = withContext(Dispatchers.IO) { call.execute() }

或者如果你正在执行一个CPU密集型的阻塞任务:

val sortedUsers = withContext(Dispatchers.Default) { users.sortByName() }

这将挂起当前的协程,然后在不同的线程上执行“不适当的阻塞调用”(来自Dispatchers)。IO或Dispatchers。(默认池),因此不会阻塞协程正在执行的线程。

其他回答

可能会发生异常,这就是为什么会显示此警告。使用runCatching{}。它捕获从块函数执行中抛出的任何Throwable异常,并将其封装为失败。

例如:

 CoroutineScope(Dispatchers.IO).launch {
         runCatching{
               makeHttpRequest(URL(downloadLocation))
         }
}

如果你选择像一些答案建议的那样压抑,那就使用

@Suppress(“BlockingMethodInNonBlockingContext”)

我今天遇到了同样的问题,下面的解决方案对我来说很有效。希望能有所帮助!

        CoroutineScope(Dispatchers.IO).launch {

        val call = client.newCall(request)
        call.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                print("Internet access, 4G, Wifi, DNS, etc failed")
            }
    
            override fun onResponse(call: Call, response: Response) {
                if(response.isSuccessful) {
                    print("Server accepted!")
                } else {
                    print("Server failed!")
                }
            }
        })
    }

请记住,这个回调函数只被使用一次。你不能在其他线程中使用它。

一个解决方案是通过挂起有趣的kotlinx. corroutine . runinterruptible来包装阻塞代码。 如果没有它,当协程的任务被取消时,阻塞代码将不会中断。

它抑制了编译警告,阻塞代码将在取消时抛出InterruptedException

val job = launch {
  runInterruptible(Dispatchers.IO) {
    Thread.sleep(500) // example blocking code
  }
}

job.cancelAndJoin() // Cause will be 'java.lang.InterruptedException'

https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-interruptible.html

如何将Java阻塞函数转换为可取消的挂起函数?

使用withContext将“不适当的阻塞方法调用”代码包装到另一个上下文中。

也就是说(例如):

如果你正在执行一个读/写阻塞方法调用:

val objects = withContext(Dispatchers.IO) { dao.getAll() }

如果您正在执行阻塞网络请求(使用Retrofit):

val response = withContext(Dispatchers.IO) { call.execute() }

或者如果你正在执行一个CPU密集型的阻塞任务:

val sortedUsers = withContext(Dispatchers.Default) { users.sortByName() }

这将挂起当前的协程,然后在不同的线程上执行“不适当的阻塞调用”(来自Dispatchers)。IO或Dispatchers。(默认池),因此不会阻塞协程正在执行的线程。