如何使“不适当的阻塞方法调用”合适?

lig*_*igi 172 kotlin okhttp moshi kotlin-coroutines

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

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

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

Evg*_*kin 61

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

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

编辑:withContext(Dispatchers.IO)或者一些自定义调度程序可以用来解决这个问题。感谢您的评论。

  • 忽略警告几乎从来都不是正确的事情,您至少可以在“withContext(Dispatchers.IO)”中运行阻塞代码。 (56认同)
  • 如果您使用“withContext(Dispatchers.IO)”运行阻塞代码,那么它就不再阻塞并且警告不正确,对吗? (25认同)
  • @Tenfour04,所以如果我们将代码包装在“withContext(Dispatchers.IO)”中,那么警告是不正确的?AndroidStudio 仍然会为我显示它,除非我放弃挂起并使用 runBlocking(Dispatchers.IO) 代替。 (21认同)
  • 添加 ```withContext(Dispatchers....)``` 并没有删除警告 (16认同)
  • 那么正确答案是什么呢?只需用“withContext(Dispatchers.IO)”包装阻塞代码并忽略警告? (9认同)
  • @noloman 好问题,因为“至少”假设提出这个问题的人不具备知识。当您引入此包装器时,警告仍然没有消失。 (8认同)
  • @noloman 阻塞调用仍然是阻塞调用,但“Dispatchers.IO”可以容忍阻塞调用,因为与“Dispatchers.Default”不同,它可以创建所需数量的线程。如果其他调度程序都因阻塞调用而被锁定,则它们可能会耗尽线程。 (3认同)
  • @Stachu 有一些像这样的误报错误报告(https://youtrack.jetbrains.com/issue/KT-39684),所以这是可能的。但是,即使对于 Dispatchers.IO,也有一些阻塞调用会故意触发警告,例如“Thread.sleep()”,这在协程中的任何位置都是一种代码味道。 (3认同)
  • @Tenfour04,[Dispatchers.IO](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-io.html)也有64个线程的限制或核心数量(以较大者为准)。 (2认同)
  • 自 IntelliJ 2022.1 起已修复。编辑甚至建议使用“withContext(Dispatchers.IO)” (2认同)

Mar*_*ark 54

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

  • 我发现有关 IOException 的解释很有帮助:https://discuss.kotlinlang.org/t/warning-inproperty-blocking-method-call-with-coroutines-how-to-fix/16903/2 (8认同)
  • 我们称之为抑制,而不是修复:)我想,静态分析器会突出显示它,因为抛出 IOException 的方法通常是阻塞的,并且需要一些宝贵的时间才能完成。 (2认同)

Sye*_*air 42

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

例如:

 CoroutineScope(Dispatchers.IO).launch {
         runCatching{
               makeHttpRequest(URL(downloadLocation))
         }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是可行的,但看起来仍然像是一种黑客攻击或一种规避 Kotlin 插件对不适当阻塞调用的错误检测的漏洞。因为本质上这个方法所做的只是将有问题的调用包装在“try ... catch”块中,这在某种程度上使其对检查不可见。 (4认同)
  • 它在 FileInputStream 和 FileOutputStream 模式下都对我有用。谢谢 (4认同)

Zul*_*fil 24

总结“不适当的阻塞方法调用” 代码另一种情况下使用withContext

也就是说(例如):

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

val objects = withContext(Dispatchers.IO) { dao.getAll() }
Run Code Online (Sandbox Code Playgroud)

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

val response = withContext(Dispatchers.IO) { call.execute() }
Run Code Online (Sandbox Code Playgroud)

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

val sortedUsers = withContext(Dispatchers.Default) { users.sortByName() }
Run Code Online (Sandbox Code Playgroud)

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

  • 我不明白。我尝试使用 Dispatchers.IO,但它仍然显示警告。我添加了“Thread.sleep”来模拟长时间等待。我希望能够通过任何可以使用线程的方式来取消它(中断并检查它是否被取消)。 (7认同)
  • 添加上面的 withContext 也不起作用 (4认同)

see*_*ess 8

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

@Suppress("BlockingMethodInNonBlockingContext")


Dew*_*eed 6

我使用的是 Android Studio 4.1,当我使用Moshi或操作File. withContext即使我确定自己在做什么,将代码包装在 a 中也无济于事。

我最近发现将警告的小代码移动到标准方法中而不是suspendfun action() {...}可以消除警告。这很丑陋,因为它只是隐藏了警告。