定义
「Kotlin 官网」
轻量级的线程。
「Android 官网」
一种并发设计模式,可以在 Android 平台上使用它来简化异步执行的代码。
「本质」
其实协程是线程的封装框架,即封装了很多线程的 API,以及多了一些其他功能。
特点
- 轻量:
可以在单个线程上运行多个协程,因为协程支持挂起,不会使正在运行协程的线程阻塞。挂起比阻塞节省内存,且支持多个并行操作。 - 内存泄漏更少:
使用结构化并发机制在一个作用域内执行多项操作。 - 内置取消支持:
取消操作会自动在运行中的整个协程层次结构内传播。 - Jetpack 集成:
许多 Jetpack 库都包含提供全面协程支持的扩展。某些库还提供自己的协程作用域,可用于结构化并发。 - 异步逻辑同步化:
对比 AsyncTask 异步任务的方法会更加具有可读性。
功能
作用同异步任务,处理耗时任务,防止主线程阻塞,常用于网络请求,处理数据库,文件读写的线程切换。
异步任务 AsyncTask
通常我们使用 AsyncTask 进行异步任务的操作,通过 doInBackground()方法请求网络或者数据库,通过 onPostExecute()方法回调得到数据,进行 UI 渲染绘制。
@SuppressLint("StaticFieldLeak")
val test = object : AsyncTask<Void, Void, String>() {
override fun doInBackground(vararg p0: Void?): String? {
//请求网络或者数据库
return "test"
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
//得到数据进行UI渲染绘制
Log.e("result===", result + "")
}
}.execute()
协程依赖
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
}
协程使用
协程的使用更加符合从上至下的逻辑,通过切换不同的调度器进行操作。
GlobalScope.launch(Dispatchers.Main) {
val result = withContext(Dispatchers.IO) {
//请求网络或者数据库
}
//得到数据进行UI渲染绘制
Log.e("result===", result.toString())
}
调度器
Dispatchers.Main
「主线程」
- UI 渲染,绘制,交互
- LiveData 更新
Dispatchers.IO
「非主线程」
- 网络请求
- 数据库操作
- 文件读取写入
Dispatchers.Default
「非主线程」
默认调度器
Dispatchers.Unconfined
「非主线程」
结构化并发
「CoroutineScope」
作用 :跟踪所有的协程,并可以取消它所启动的所有协程。
常用 Scope
- GlobalScope
生命周期是 process 级别,页面关闭,协程依然运行。 - MainScope
在 Activity 中使用,可在页面的 OnDestory()方法中取消。 - ViewModelScope
只能在 ViewModel 中使用,绑定 ViewModel 的生命周期。 - LifeCycleScope
只能在 Activity 和 Fragment 中使用,绑定其生命周期。
构建器
launch
返回 Job,不附带任何结果值。配合 join()方法进行等待协程作业。
async
返回 Deferred(继承 Job),可以使用 await()获取结果。配合 await()方法进行等待协程作业。
Job 生命周期
Job 是协程的唯一标志符,负责管理协程的生命周期。
| 生命周期 | 状态 |
|---|---|
| New | 新创建 |
| Active | 活跃 |
| Completing | 完成中 |
| Completed | 已完成 |
| Cancelling | 取消中 |
| Cancelled | 已取消 |
挂起和恢复
suspend
「挂起」
暂停执行当前协程,并保存方法内的所有局部变量。
「挂起函数」
使用 suspend 关键字修饰的函数,只能在挂起函数或者协程体内调用。
「挂起和阻塞的区别」
挂起是开启新线程进行操作,线程里面进行长时间操作不会造成主线程的阻塞。
阻塞是在主线程进行操作,需要完成一个任务之后再做下一个任务,多个任务等待就会存在阻塞现象。
resume
「恢复」
唤醒状态机,重新执行协程体的代码。
启动模式
CoroutineStart.DEFAULT
饿汉式启动,创建后立即开始调度,如果使用 cancel()可以直接取消协程运行。
CoroutineStart.LAZY
懒汉式启动,创建了,但是需要主动调用 start(),await(),join()才可以执行调度及运行。
CoroutineStart.ATOMIC
创建后立即开始调度,使用 cancel()在第一个挂起点前不能被取消。
CoroutineStart.UNDISPATCHED
直接开始在当前线程下执行,直到遇到第一个挂起点。
作用域构建器
coroutineScope
包裹多个协程,如果其中一个协程失败了,其他的协程同步失败。
supervisorScope
包裹多个协程,如果其中一个协程失败了,其他的协程依然执行。
取消协程
前提:多个协程在同一作用域的情况下。
- 取消作用域会取消所有的子协程。
- 取消某个子协程不会取消它的兄弟协程。
- 协程通过抛出 CancellationException 来处理取消协程。
- 所有的挂起函数都是可以取消的。
CPU 密集型任务取消
- isActive()
coroutineScope 的扩展属性,判断 Job 的活跃状态。 - ensureActive()
执行,本质是 isActive(),如果 Job 状态为非活跃,直接抛出错误。 - yield()
检查 Job 的活跃状态,若已取消则抛出异常,也会出让执行权给其他协程,避免出现资源耗尽的情况。
副作用处理
- 在 finally 中是否资源。
- 使用 use()函数,自带资源关闭,常用于文件读取。
不能取消执行的代码
- 使用 withContext(NonCancellable){}函数。
超时
- 使用 withTimeout(){}/withTimeoutOrNull(){} 函数。