新收藏CoroutineScope

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,例如必须在整个应用生命周期过程中保持活动的顶层后台进程。

参考:Kotlin Coroutines 1.5: 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() }
}
上一篇下一篇

猜你喜欢

热点阅读