如何使 "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.IO
或 Dispatchers.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")
}
我目前正在尝试更多地利用 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.IO
或 Dispatchers.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")
}