IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Kotlin CoroutineContext 协程上下文 -> 正文阅读

[移动开发]Kotlin CoroutineContext 协程上下文

Kotlin CoroutineContext 协程上下文

概述

  • CoroutineContext即协程上下文,可以用来切换线程池、指定协程名、捕获异常等。
  • CoroutineContext是一个接口,如Job、Deferred、Dispatcher、CoroutineName、CoroutineExceptionHandler都间接继承自CoroutineContext接口。
  • CoroutineScope协程作用域,用于批量控制协程,本质也是对CoroutineContext的一层简单封装。

在这里插入图片描述

Dispatchers 协程调度器

协程是运行在线程之上的。

  • Dispatchers.Main:只在UI编程平台才有意义,如Android,一般Main线程用于UI绘制。在普通JVM工程中无法直接使用。
  • Dispatchers.Unconfined:代表无所谓,当前协程可能运行在任意线程之上,不推荐使用。
  • Dispatchers.Default:CPU密集型任务的线程池。
  • Dispatchers.IO:IO密集型任务的线程池。

情况一

fun main() = runBlocking {
    val user = getUserInfo()
    log(user)
}

suspend fun getUserInfo(): String {
    log("before")
    withContext(Dispatchers.IO) {
        log("withContext(Dispatchers.IO)")
        delay(1000L)
    }
    log("after")
    return "hello world"
}

/*
输出信息:
┌──────────────────────────────────────────────────
before
Thread:main @coroutine#1
└──────────────────────────────────────────────────
┌──────────────────────────────────────────────────
withContext(Dispatchers.IO)
Thread:DefaultDispatcher-worker-1 @coroutine#1
└──────────────────────────────────────────────────
┌──────────────────────────────────────────────────
after
Thread:main @coroutine#1
└──────────────────────────────────────────────────
┌──────────────────────────────────────────────────
hello world
Thread:main @coroutine#1
└──────────────────────────────────────────────────
*/

说明:使用withContext(Dispatchers.IO)后,withContext中的代码会被分发到DefaultDispatcher线程池中执行,而它外部的代码依然在main线程中执行。

情况二

//						   变化
//							↓
fun main() = runBlocking(Dispatchers.Default) {
    val user = getUserInfo()
    log(user)
}

suspend fun getUserInfo(): String {
    log("before")
    withContext(Dispatchers.IO) {
        log("withContext(Dispatchers.IO)")
        delay(1000L)
    }
    log("after")
    return "hello world"
}

/*
输出信息:
┌──────────────────────────────────────────────────
before
Thread:DefaultDispatcher-worker-1 @coroutine#1
└──────────────────────────────────────────────────
┌──────────────────────────────────────────────────
withContext(Dispatchers.IO)
Thread:DefaultDispatcher-worker-2 @coroutine#1
└──────────────────────────────────────────────────
┌──────────────────────────────────────────────────
after
Thread:DefaultDispatcher-worker-2 @coroutine#1
└──────────────────────────────────────────────────
┌──────────────────────────────────────────────────
hello world
Thread:DefaultDispatcher-worker-2 @coroutine#1
└──────────────────────────────────────────────────
*/

说明:当Default线程池有富余线程时,它是可以被IO线程池复用的。

情况三

val mySingleDispatcher: ExecutorCoroutineDispatcher = Executors.newSingleThreadExecutor {
    Thread(it, "我的单线程").apply { isDaemon = true }
}.asCoroutineDispatcher()

//						   变化
//   						↓
fun main() = runBlocking(mySingleDispatcher) {
    val user = getUserInfo()
    log(user)
}

suspend fun getUserInfo(): String {
    log("before")
    withContext(Dispatchers.IO) {
        log("withContext(Dispatchers.IO)")
        delay(1000L)
    }
    log("after")
    return "hello world"
}

/*
输出信息:
┌──────────────────────────────────────────────────
before
Thread:我的单线程 @coroutine#1
└──────────────────────────────────────────────────
┌──────────────────────────────────────────────────
withContext(Dispatchers.IO)
Thread:DefaultDispatcher-worker-1 @coroutine#1
└──────────────────────────────────────────────────
┌──────────────────────────────────────────────────
after
Thread:我的单线程 @coroutine#1
└──────────────────────────────────────────────────
┌──────────────────────────────────────────────────
hello world
Thread:我的单线程 @coroutine#1
└──────────────────────────────────────────────────
*/

说明:创建一个单线程的线程池,为runBlocking传入自定义的mySingleDispatcher后,程序运行在mySingleDispatcher线程池中,而withContext中的代码仍然运行在DefaultDispatcher线程池中。

CoroutineScope 协程作用域

CoroutineScope是一个接口,内部有个CoroutineContext类型的成员变量。CoroutineScope有个扩展函数launch,用于开启一个协程。

CoroutineScope最大的作用是方便我们批量控制协程。

//CoroutineScope相关源码

public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
}

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    ...
}

情况一

fun main() = runBlocking {
    val scope: CoroutineScope = CoroutineScope(Job())
    scope.launch {
        log("协程A start")
        delay(1000L)
        log("协程A end") //不会执行
    }
    scope.launch {
        log("协程B start")
        delay(1000L)
        log("协程B end") //不会执行
    }
    scope.launch {
        log("协程C start")
        delay(1000L)
        log("协程C end") //不会执行
    }
    delay(500L)
    scope.cancel()
}

/*
输出信息:
┌──────────────────────────────────────────────────
协程A start
Thread:DefaultDispatcher-worker-1 @coroutine#2
└──────────────────────────────────────────────────
┌──────────────────────────────────────────────────
协程B start
Thread:DefaultDispatcher-worker-2 @coroutine#3
└──────────────────────────────────────────────────
┌──────────────────────────────────────────────────
协程C start
Thread:DefaultDispatcher-worker-3 @coroutine#4
└──────────────────────────────────────────────────
 */

说明:创建一个CoroutineScope对象,使用这个scope开启三个协程,500毫秒后,调用cancel()方法,这三个协程被取消了。这也体现了Kotlin协程结构化并发的特性。

Job与CoroutineContext关系

Job继承自CoroutineContext.Element,而CoroutineContext.Element继承自CoroutineContext,所以Job间接继承自CoroutineContext。

Job也是一个CoroutineContext类型。

//Job相关源码:

public interface Job : CoroutineContext.Element { }

public interface CoroutineContext {   
    public operator fun <E : Element> get(key: Key<E>): E?
    public operator fun plus(context: CoroutineContext): CoroutineContext {}
    public fun <R> fold(initial: R, operation: (R, Element) -> R): R
    public fun minusKey(key: Key<*>): CoroutineContext

    public interface Element : CoroutineContext { }
}
@OptIn(ExperimentalStdlibApi::class)
fun main() = runBlocking {
    val scope: CoroutineScope = CoroutineScope(Job() + mySingleDispatcher)
    val job: Job = scope.launch {
        log("协程A start")
        log(coroutineContext[CoroutineDispatcher] == mySingleDispatcher)
        delay(1000L)
        log("协程A end") //不会执行
    }
    delay(500L)
    scope.cancel()
}

/*
输出信息:
┌──────────────────────────────────────────────────
协程A start
Thread:我的单线程 @coroutine#2
└──────────────────────────────────────────────────
┌──────────────────────────────────────────────────
true
Thread:我的单线程 @coroutine#2
└──────────────────────────────────────────────────
 */

说明:CoroutineContext可以看作Map,内部进行了一些操作费重载。

Dispatcher与CoroutineContext关系

Dispatchers是一个object单例,内部成员是CoroutineDispatcher类型,而它继承自ContinuationInterceptor接口,这个接口继承自 CoroutineContext.Element,所以Dispatchers间接继承自CoroutineContext。

Dispatcher也是一个CoroutineContext类型。

//Dispatcher相关源码:

public actual object Dispatchers {
    public actual val Default: CoroutineDispatcher = DefaultScheduler   
    public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
    public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
    public val IO: CoroutineDispatcher = DefaultIoScheduler
    public fun shutdown() { }
}

public abstract class CoroutineDispatcher : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor { }

public interface ContinuationInterceptor : CoroutineContext.Element { }

CoroutineName 指定协程名

CoroutineName是一个数据类,继承自AbstractCoroutineContextElement类,而它继承自Element,所以CoroutineName间接继承自CoroutineContext。

CoroutineName也是一个CoroutineContext类型。

//CoroutineName相关源码:
public data class CoroutineName(val name: String) : AbstractCoroutineContextElement(CoroutineName) { }

public abstract class AbstractCoroutineContextElement(public override val key: Key<*>) : Element
@OptIn(ExperimentalStdlibApi::class)
fun main() = runBlocking {
    val scope: CoroutineScope = CoroutineScope(Job() + mySingleDispatcher + CoroutineName("我的协程"))
    scope.launch {
        log("协程A start")
        log(coroutineContext[CoroutineDispatcher] == mySingleDispatcher)
        delay(1000L)
        log("协程A end")
    }
    delay(500L)
    scope.cancel()
}

/*
输出信息:
┌──────────────────────────────────────────────────
协程A start
Thread:我的单线程 @我的协程#2
└──────────────────────────────────────────────────
┌──────────────────────────────────────────────────
true
Thread:我的单线程 @我的协程#2
└──────────────────────────────────────────────────
 */

CoroutineExceptionHandler 协程的异常处理

CoroutineExceptionHandler间接继承自CoroutineContext。

CoroutineExceptionHandler也是一个CoroutineContext类型。

//CoroutineExceptionHandler相关源码:

public inline fun CoroutineExceptionHandler(crossinline handler: (CoroutineContext, Throwable) -> Unit): CoroutineExceptionHandler =
object : AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler {
    override fun handleException(context: CoroutineContext, exception: Throwable) =
    handler.invoke(context, exception)
}

public interface CoroutineExceptionHandler : CoroutineContext.Element {
    public companion object Key : CoroutineContext.Key<CoroutineExceptionHandler>
    public fun handleException(context: CoroutineContext, exception: Throwable)
}
fun main() = runBlocking {
    val myExceptionHandler = CoroutineExceptionHandler { 
        coroutineContext, throwable -> println("捕获异常:$throwable")
    }
    val scope: CoroutineScope = CoroutineScope(Job())
    val job = scope.launch(myExceptionHandler) {
        val s: String? = null
        s!!.length
    }
    job.join()
}

/*
输出信息:
捕获异常:java.lang.NullPointerException
 */
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-01 00:13:15  更:2022-04-01 00:13:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 20:13:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码