android Kotlin原理
目录
一,概述
1.1 kotlin协程序原理:
1.2 核心概念
二,协程调度器之Dispatchers
三,协程能进行线程恢复的原理
一,概述
1.1 kotlin协程序原理:
1,内部线程池管理线程使用到了自旋和挂起
2,传统的线程之所以重,是因为线程的执行,等待唤醒需要操作系统来完成
3,协程之所以相对于传统的线程轻量级,是因为协程是通过协程调度器来完成
线程的唤醒,调度,执行的,区别就在哪里
4,协程是基于线程的,没有线程就没有协程,协程最终是通过开启线程,thread,调用
thread的start方法完成协程的调度的
协程是一种并发设计模式,您可以在 Android 平台上使用它来简化异步执行的代码。协程 是在 1.3 版中添加到 Kotlin 的,基于既定的 从其他语言转换成的概念。
在 Android 上,协程有助于管理长时间运行的任务,如果管理不当,这些任务可能会阻塞主线程并导致应用无响应。使用协程的专业开发者中有超过 50% 的人反映使用协程提高了工作效率。本主题介绍如何使用 Kotlin 协程解决以下问题,从而让您能够编写出更清晰、更简洁的应用代码。
协程是我们在 Android 上进行异步编程的推荐解决方案。值得关注的特点包括:
- 轻量:您可以在单个线程上运行多个协程,因为协程支持挂起,不会使正在运行协程的线程阻塞。挂起比阻塞节省内存,且支持多个并行操作。
- 内存泄漏更少:使用 结构化并发 在一个范围内运行多项操作
- 内置取消支持: 取消 通过正在运行的协程层次结构自动传播。
- Jetpack 集成:许多 Jetpack 库都包含提供全面协程支持的扩展。某些库还提供自己的协程作用域,可供您用于结构化并发。
1.2 核心概念
以上代码就涉及到了协程的四个基础概念:
- suspend function。即挂起函数,delay() 就是协程库提供的一个用于实现非阻塞式延时的挂起函数
- CoroutineScope。即协程作用域,GlobalScope 是 CoroutineScope 的一个实现类,用于指定协程的作用范围,可用于管理多个协程的生命周期,所有协程都需要通过 CoroutineScope 来启动
- CoroutineContext。即协程上下文,包含多种类型的配置参数。
Dispatchers.IO
就是 CoroutineContext 这个抽象概念的一种实现,用于指定协程的运行载体,即用于指定协程要运行在哪类线程上 - CoroutineBuilder。即协程构建器,协程在 CoroutineScope 的上下文中通过 launch、async 等协程构建器来进行声明并启动。launch、async 均被声明为 CoroutineScope 的扩展方法
1.3 kotlin协程调试办法
设置VM参数:-Dkotlinx.coroutines.debug
Android工程中打开协程debug模式,无法直接在工程中进行设置,而是需要在Android代码中设置相应属性。设置代码如下:
System.setProperty("kotlinx.coroutines.debug", "on" )
上面的对kotlin版本有要求,最好1.4以上
二,协程调度器之Dispatchers
三,协程能进行线程恢复的原理
kotlinx.coroutines.CoroutineContextKt
restoreThreadContext
这里是最核心的,这段代码是在Thread的run方法中被调用,在开始调用之前调用updateThreadContext(context, countOrElement)方法把当前线程的数据保存起来,然后在调用业务的 block函数,这里的context, countOrElement这两个参数是调用线程的数据和上下文(比如说在主线程中调用,那么这个数据就是主线程的),然后在block函数执行完成以后,在finally {
restoreThreadContext(context, oldValue)
}
调用restoreThreadContext(context, oldValue) 也就是恢复之前的数据,在这里完成了线程的数据切换(线程栈数据切换,保存的是线程堆栈信息)
internal fun updateThreadContext(context: CoroutineContext, countOrElement: Any?): Any? {
@Suppress("NAME_SHADOWING")
val countOrElement = countOrElement ?: threadContextElements(context)
@Suppress("IMPLICIT_BOXING_IN_IDENTITY_EQUALS")
return when {
countOrElement === 0 -> ZERO // very fast path when there are no active ThreadContextElements
// ^^^ identity comparison for speed, we know zero always has the same identity
countOrElement is Int -> {
// slow path for multiple active ThreadContextElements, allocates ThreadState for multiple old values
context.fold(ThreadState(context, countOrElement), updateState)
}
else -> {
// fast path for one ThreadContextElement (no allocations, no additional context scan)
@Suppress("UNCHECKED_CAST")
val element = countOrElement as ThreadContextElement<Any?>
element.updateThreadContext(context)
}
}
}
这里面是最核心的部分,使用了ThreadLocal来存储不同线程的数据,这个才是最核心的部分,只有这样子,那么在在从线程挂起到恢复的时候,才知道调用栈地址以及数据
internal class ThreadLocalElement<T>(
private val value: T,
private val threadLocal: ThreadLocal<T>
) : ThreadContextElement<T> {
override val key: CoroutineContext.Key<*> = ThreadLocalKey(threadLocal)
override fun updateThreadContext(context: CoroutineContext): T {
val oldState = threadLocal.get()
threadLocal.set(value)
return oldState
}
override fun restoreThreadContext(context: CoroutineContext, oldState: T) {
threadLocal.set(oldState)
}