一,什么是协程:
官方的介绍:
协程是一种并发设计模式,在Android平台上可以使用协程简化异步执行代码。(协程可以使我们是同步的代码形式实现异步任务的执行)
协程的特点:
- 轻量:一个线程上可以运行多个协程,协程支持挂起,协程的挂起不会阻塞运行协程的线程。挂起比阻塞更节省内存,也能让线程得到充分的利用。
- 内存泄漏更少:使用结构化并发在一个范围内(协程作用域)运行多项操作。
- 内置取消支持:取消通过正在运行的协程层次结构,自动传播。
- Jetpack集成:JetPack库提供了全面支持协程的扩展,提供了类似LifecycleScope,ViewModelScope绑定生命周期的作用域。
二,协程的使用:
1,添加相关依赖:
project的build.gradle中添加Kotlin编译插件
js
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.32"
}
app的build.gradle中添加协程相关的依赖库。
js
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.32"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3"
}
2,构建启动一个协程
构建启动协程常用三种方式:
runBlocking:创建并启动一个新的线程,并且会阻塞当前的线程,直到协程内所有逻辑 和 子协程都执行完毕。项目中用不了,主要在@test测试代码中使用。
launch: 启动创建一个新的协程而不阻塞当前的线程。返回一个协程引用Job对象。
js
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
context: 协程的上下文,协程的运行环境。可以是多个上下文组合,包括调度器(Dispatchers.Default)、协程本身的job、协程的名称(CoroutineName)、异常处理器(CoroutineExceptionHandler)
start: 协程的启动模式,立即启动 或 懒启动(Lazy)
block:是一个无参、无返回值的挂起函数,我们业务逻辑实现方法。
Job:协程的任务对象,可以通过Job 获取协程的状态和对协程执行操作。isActive()判断是否活跃,start()启动协程,cancel取消协程,join()挂起协程直到此job执行完成等,
java
fun test(){
val job = MainScope().launch{
Log.i("wang", "launch")
delay(1000)//挂起函数
Log.i("wang", "after one second")
//两个launch构建并启动
val job1= launch {
delay(3000)
Log.i("wang", "launch1")
}
launch {
delay(3000)
Log.i("wang", "launch2")
}
job1.join()//等待协程执行完,也是挂起函数,非阻塞的
launch {
delay(2000)
Log.i("wang", "launch3")
}
}
Log.i("wang", "end")
}
运行结果:
js
2025-04-28 09:53:32.449 14296-14296 wang I end
2025-04-28 09:53:32.452 14296-14296 wang I launch
2025-04-28 09:53:33.454 14296-14296 wang I after one second
2025-04-28 09:53:36.467 14296-14296 wang I launch1
2025-04-28 09:53:36.471 14296-14296 wang I launch2
2025-04-28 09:53:38.480 14296-14296 wang I launch3
async:启动创建一个新的协程而不阻塞当前的线程。返回一个协程引用Deffered对象。
js
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
block: 无参、有返回值T的挂起函数,较launch多了返回值。
Deffered: 是Job的子类,较Job多了个await()挂起函数,用于等待协程执行的结果返回值。
java
fun testAsync(){
MainScope().launch {
val deferred1 = async {
delay(1000)
Log.i("wang", "deferred1 execute")
100
}
val deferred2 = async {
delay(2000)
Log.i("wang", "deferred2 execute")
200
}
val deferred3 = async {
delay(3000)
Log.i("wang", "deferred3 execute")
300
}
Log.i("wang", "sum = " + (deferred1.await() + deferred2.await() + deferred3.await()))
}
}
运行结果:
js
2025-04-27 18:28:37.134 9747-9747 wang com.example.myapplication I deferred1 execute
2025-04-27 18:28:38.134 9747-9747 wang com.example.myapplication I deferred2 execute
2025-04-27 18:28:39.136 9747-9747 wang com.example.myapplication I deferred3 execute
2025-04-27 18:28:39.137 9747-9747 wang com.example.myapplication I sum = 600
3,协程的作用域
GlobalScope:作用域是全局,与应用程序的生命周期绑定。
coroutineScope: 创建一个独立协程作用域,所有的子协程都执行完了以后才结束自身,当前有一个子协程抛出异常后,所有未执行完成的子协程+自身都会停止执行。
superVisorScope: 同上类似,不同是子协程出现异常,不会影响父协程,也不会影响同阶的子协程。相当于在launch(SuperVisorJob())
MainScope():上下文是SupervisorJob() + Dispatchers.Main。一个在主线程执行的协程作用域。
lifecycleScope:具有生命周期感知的协程作用域,与Lifecycle生命周期绑定,生命周期结束时,此作用域将被取消。
viewModelScope: 与lifecycleScope类似,与ViewModel的生命周期绑定。
4,协程调度器
Kotlin 提供了四个调度器,您可以使用它们来指定应在何处运行协程:
调度器 | 介绍 |
---|---|
Dispatchers.Default | 默认调度器,非主线程。CPU 密集型任务调度器。用于json解析,数据处理 |
Dispatchers.Main | UI 调度器, Andorid 上的主线程。用于UI更新 |
Dispatchers.Unconfined | 非指定线程的协程调度器,取决于启动的线程。 |
Dispatchers.IO | IO 调度器,非主线程,执行的线程是IO 线程。用于网络请求,数据库处理、文件读写 |
5,suspend挂起函数
suspend:是kotlin的核心关键字,使用suspend关键字修饰的函数叫挂起函数,挂起函数只能在协程中或其他挂起函数中执行。
协程在执行到有suspend
标记的挂起函数时,当前函数会被挂起(暂停),直到该挂起函数内部逻辑完成,才会在挂起的地方resume
恢复继续执行,而不会阻塞运行协程的线程。
java
private fun testSuspend(){
//启动了一个协程,去执行一个挂起函数
Log.i("wang", "start")
MainScope().launch {
Log.i("wang", "start")
val result = doWorkBackGroup()
Log.i("wang", "result$result")
}
}
private suspend fun doWorkBackGroup(): String{
//模拟耗时任务
delay(1000)
Log.i("wang", "doWorkBackGroup end")
return "hello world"
}
执行结果如下:
js
2025-04-28 17:34:21.049 27706-27706 wang I start
2025-04-28 17:34:22.051 27706-27706 wang I doWorkBackGroup end
2025-04-28 17:34:22.052 27706-27706 wang I resulthello world
源码分析:KotlinBytecode后在deCompile后的代码如下,多了一个编译器加的参数Continuation,有个接口方法resumeWith(result), 即挂起函数执行完毕后恢复的回调。
java
private final Object doWorkBackGroup(Continuation $completion) {
kotlin
@kotlin.SinceKotlin public interface Continuation<in T> {
public abstract val context: kotlin.coroutines.CoroutineContext
public abstract fun resumeWith(result: kotlin.Result<T>): kotlin.Unit
}