如何使 "inappropriate blocking method call" 合适?

How to make "inappropriate blocking method call" appropriate?

我目前正在尝试更多地利用 kotlin 协程。但我遇到了一个问题:在这些协同程序中使用 moshi 或 okhttp 时,我收到警告:

"inappropriate blocking method call"

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

警告是关于阻塞当前线程和协程的方法不能被正确挂起。这样,您将失去协程的所有好处,并再次降级为每个线程一个作业。

每种情况都应该以不同的方式处理。对于可暂停的 http 调用,您可以使用 ktor http client。但有时没有适合您情况的库,因此您可以编写自己的解决方案或忽略此警告。

编辑:withContext(Dispatchers.IO) 或某些自定义调度程序可用于解决该问题。感谢评论。

调用带有 @Throws(IOException::class) 注释的挂起函数时,您也会收到此警告 (Kotlin 1.3.61)。不确定这是否有意。无论如何,您可以通过删除该注释或将其更改为 Exception class.

来抑制此警告

Wrap “不恰当的阻塞方法调用” code in 另一个上下文 使用 withContext.

也就是说(举例):

如果您正在执行 read/write 阻塞方法调用:

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

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

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

或者,如果您正在执行 CPU 密集阻塞任务:

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

这将挂起当前协程,然后在不同的线程(来自 Dispatchers.IODispatchers.Default 池中执行 “不适当的阻塞调用” ),因此不会阻塞你的协程正在执行的线程。

如果您确实像某些答案所建议的那样选择抑制,请使用

@Suppress("BlockingMethodInNonBlockingContext")

我正在使用 Android Studio 4.1,当我使用 Moshi 或操作 File 时出现警告。将代码包装在 withContext 中无济于事,即使我确定自己在做什么。

我最近发现将 警告 的小代码移动到没有 suspend 的标准方法中,如 fun action() {...} 可以删除警告。这很难看,因为它只是隐藏了警告。

更新:根据我个人的经验,似乎抑制警告或 runBlocking 更直接。

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

例如:

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

我使用调度程序作为启动参数:

    GlobalScope.launch(Dispatchers.IO) {
        // Do background work
        
        // Back to main thread
        launch(Dispatchers.Main) {
            Toast.makeText(context, "SUCCESS!", Toast.LENGTH_LONG)
                .show()
        }
    }

您收到此警告是因为协程永远不应阻塞,而应暂停

这就是您暂停协程的方式,运行 您在线程中的阻塞方法,并在结果上恢复它。这还将处理异常,因此您的应用不会崩溃。

suspendCoroutine { continuation ->
    thread {
        try {
            doHttpRequest(URL(...)) {
                continuation.resume(it)
            }
        }
        catch (t: Throwable) {
            continuation.resumeWithException(t)
        }
    }
}

看起来将调用封装在 kotlin.runCatching() 中可以解决警告,但不确定为什么...因为正如之前关于 runCatching 的回答所述,这不是由于异常抛出,因为即使 try{} catch 也没有无法解决问题,可能是一些错误检测问题... 我现在最终使用了下面的...

val result = kotlin.runCatching {
     OldJavaLib.blockingCallThatThrowsAnException()
}
if (result.isSuccess) {
      print("success is on your side")
} else {
      print("one failure is never the end")
}