kotlin GlobalScope 警告,处理协程异常
2021-09-13 本文已影响0人
goodl
GlobalScope 类已被 @DelicateCoroutinesApi 注解所标记。 代码中会报警告:
This is a delicate API and its use requires care. Make sure you fully read and understand documentation of the declaration that is marked as a delicate API.
译文: 这是一个精致的API,需要谨慎使用。请确保您完全阅读并理解标记为精致API的声明的文档。
虽然大多数情况下都不建议使用 GlobalScope
,但在一些情况下确实很方便。在有限的场景下,可以合理且安全地使用 GlobalScope
,例如必须在整个应用生命周期过程中保持活动的顶层后台进程。
替代方案:自己实现一个 CoroutineScope:
@file:JvmName("ThreadUtil")
import android.os.Looper
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.launch
import java.io.Closeable
import java.util.concurrent.atomic.AtomicReference
import kotlin.coroutines.AbstractCoroutineContextElement
import kotlin.coroutines.CoroutineContext
// 保存 CoroutineScope
private var scopeRef: AtomicReference<Any> = AtomicReference()
// 自定义的 CoroutineScope
val appGlobalScope: CoroutineScope
get() {
while (true) {
val existing = scopeRef.get() as CoroutineScope?
if (existing != null) {
return existing
}
val newScope = SafeCoroutineScope(Dispatchers.Main.immediate)
if (scopeRef.compareAndSet(null, newScope)) {
return newScope
}
}
}
// 不会崩溃的 CoroutineScope
private class SafeCoroutineScope(context: CoroutineContext) : CoroutineScope, Closeable {
override val coroutineContext: CoroutineContext =
SupervisorJob() + context + UncaughtCoroutineExceptionHandler()
override fun close() {
coroutineContext.cancelChildren()
}
}
// 自定义 CoroutineExceptionHandler
private class UncaughtCoroutineExceptionHandler : CoroutineExceptionHandler,
AbstractCoroutineContextElement(CoroutineExceptionHandler) {
override fun handleException(context: CoroutineContext, exception: Throwable) {
// 处理异常
}
}
val isOnMainThread: Boolean
get() = Looper.myLooper() == Looper.getMainLooper()
fun runOnMainThread(block: () -> Unit) {
if (isOnMainThread) block.invoke()
else appGlobalScope.launch(Dispatchers.Main) { block.invoke() }
}