协程是一种非抢占式或者说协作式的计算机程序并发调度的实现,程序可以主动挂起或者恢复执行。
函数或者一段程序能够被挂起(可以理解成暂停),待会儿再恢复,挂起和恢复是开发者的程序逻辑自己控制的,协程是通过主动挂起出让运行权来实现协作的。
协程和线程区别线程之间是抢占式的调度协程之间是协作式的调度
协程是依赖于线程,但是协程挂起时不需要阻塞线程,几乎是无代价的。所以协程像是一种用户态的线程,非常轻量级,一个线程中可以创建N个协程。
使用 依赖// 协程核心库 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3" // 协程Android支持库 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3"开启协程 使用 runBlocking 顶层函数
通常适用于单元测试的场景,业务开发中不会用到这种方法,因为它是线程阻塞的。
runBlocking { // 代码... }使用GlobalScope 对象
使用GlobalScope单例对象,可以直接调用launch、async开启协程。在Android开发中同样不推荐这种用法,因为它的生命周期会和app一致,且不能取消(和使用runBlocking的区别在于不会阻塞线程)。
GlobalScope.launch { // 代码... } GlobalScope.async { // 代码... }使用CoroutineScope 对象
通过 CoroutineContext创建一个CoroutineScope对象,需要一个类型为CoroutineContext 的参数(推荐的使用,通过 context 参数去管理和控制协程的生命周期)。
val coroutineScope = CoroutineScope(context) coroutineScope.launch { // 代码... }作用域
作用域(Coroutine Scope)是协程运行的作用范围。launch、async都是CoroutineScope的扩展函数,CoroutineScope定义了新启动的协程作用范围,同时会继承了他的coroutineContext自动传播其所有的 elements和取消 *** 作。
实 *** runBlocking 顶级函数binding.tvBtn.setOnClickListener { val result= runBlocking { Log.e("CoroutineExActivity","---runBlocking--》启动协程") "使用runBlocking 启动协程" } Log.e("CoroutineExActivity","--runBlocking-->${result}") } // 执行结果 CoroutineExActivity: ---runBlocking--》启动协程 CoroutineExActivity: --runBlocking-->使用runBlocking 启动协程
runBlocking启动的是一个新的协程并阻塞调用它的线程,直到runBlocking运行结束才继续往下执行。并且在runBlocking协程最后一行增加一个返回值。
launch 函数binding.tvBtn.setOnClickListener { val result=GlobalScope.launch { Log.e("CoroutineExActivity","-----》启动协程") } Log.e("CoroutineExActivity","---->${result}") } // 执行结果(其中一种结果) CoroutineExActivity: -----》启动协程 CoroutineExActivity: ---->StandaloneCoroutine{Completed}@b2fd55f
从上面的执行结果来看,launch方法返回StandaloneCoroutine类型的result。查看一下其源码:
// launch 源码 public fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit ): Job { val newContext = newCoroutineContext(context) val coroutine = if (start.isLazy) LazyStandaloneCoroutine(newContext, block) else StandaloneCoroutine(newContext, active = true) coroutine.start(start, coroutine, block) return coroutine }
从源码来看,launch方法返回了Job类型。那执行结果StandaloneCoroutine和源码中定义方法返回类型Job之间有啥关联?查看StandaloneCoroutine源码:
// StandaloneCoroutine 源码 private open class StandaloneCoroutine( parentContext: CoroutineContext, active: Boolean ) : AbstractCoroutine(parentContext, active) { ... } public abstract class AbstractCoroutine ( @JvmField protected val parentContext: CoroutineContext, active: Boolean = true ) : JobSupport(active), Job, Continuation , CoroutineScope { ... }
从源码来看,StandaloneCoroutine是Job 的子类。
async 函数binding.tvBtn.setOnClickListener { val result=GlobalScope.async { Log.e("CoroutineExActivity","---async--》启动协程") } Log.e("CoroutineExActivity","--async-->${result}") } // 执行结果(其中一种结果) CoroutineExActivity: --async-->DeferredCoroutine{Active}@b2fd55f CoroutineExActivity: ---async--》启动协程
从上面的执行结果来看,async方法返回DeferredCoroutine类型的result。查看一下其源码:
public funCoroutineScope.async( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> T ): Deferred { val newContext = newCoroutineContext(context) val coroutine = if (start.isLazy) LazyDeferredCoroutine(newContext, block) else DeferredCoroutine (newContext, active = true) coroutine.start(start, coroutine, block) return coroutine } private open class DeferredCoroutine ( parentContext: CoroutineContext, active: Boolean ) : AbstractCoroutine (parentContext, active), Deferred , SelectClause1 { ... } public interface Deferred : Job { //返回结果值,或者如果延迟被取消,则抛出相应的异常 public suspend fun await(): T public val onAwait: SelectClause1 public fun getCompleted(): T public fun getCompletionExceptionOrNull(): Throwable? }
从上面的源码来看,DeferredCoroutine是AbstractCoroutine的实现类,同时实现Deferred 接口。这么看来DeferredCoroutine就是一个Deferred,一个携带有返回值Job。 通过
Deferred 的await() 方法来获取返回值T。
// launch 源码 public fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit ): Job { ... } // async 源码 public funCoroutineScope.async( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> T ): Deferred { ... }
从launch和 async 方法源码来看,CoroutineContext代表上下文,它的作用就是线程切换,它的值有Dispatchers.Main、Dispatchers.IO、、Dispatchers.Default、Dispatchers.Unconfined。
CoroutineStart表示启动模式,其值有:CoroutineStart.DEFAULT、CoroutineStart.LAZY、CoroutineStart.ATOMIC、CoroutineStart.UNDISPATCHED,默认直接允许调度执行。各个值具体含义和用法在稍后文章中讲解。
// 第一种写法 coroutineScope.launch(Dispatchers.Main) { // 在 UI 线程开始 val image = withContext(Dispatchers.IO) { // 切换到 IO 线程,并在执行完成后切回 UI 线程 getImage(imageId) // 将会运行在 IO 线程 } avatarIv.setImageBitmap(image) // 回到 UI 线程更新 UI } // 第二种方法 coroutineScope.launch(Dispatchers.Main) {// 在 UI 线程开始 val image = getImage(imageId) // 调用suspend 函数 avatarIv.setImageBitmap(image) // 回到 UI 线程更新 UI } // 挂起函数 suspend fun getImage(imageId: Int) = withContext(Dispatchers.IO) { ... }
suspend 是 Kotlin 协程最核心的关键字,意思是可挂起(暂停)。挂起函数只能在协程中或者挂起函数中调用。
withContext()函数是一个比较特殊的作用域构建器,withContext()函数是一个挂起函数。调用
withContext()函数之后,会立即执行代码块中的代码,同时将当前协程阻塞住。当代码块中的代码全部执行完之后,会将最后一行的执行结构作为withContext()函数的返回值返回。
编程语言级别实现的协程就是程序内部的逻辑,不会涉及 *** 作系统的资源之间的切换。 *** 作系统的内核线程自然会重一些,且不说每创建一个线程就会开辟的栈带来的内存开销,线程在上下文切换的时候需要CPU把高速缓存清掉并从内存中替换下一个线程的内存数据,并且处理上一个内存的中断点保存就是一个开销很大的事儿。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)