协程构建器
launch和async构建器都用来启动新协程。
- launch,返回一个Job并且不附带任何结果。
- async,返回一个Deferred,Deferred也是一个Job,可以使用.await()在一个延期的值上得到最终的结果。
- launch 是非阻塞的。
- runBlocking 是阻塞的,一般用在测试中,会阻塞当前线程,会等到包裹的子协程都执行完毕才退出。
- 多个 withContext 任务是串行的, 且withContext 可直接返回耗时任务的结果。
- 多个 async 任务是并行的,async 返回的是一个Deferred<T>,需要调用其await()方法获取结果。await() 只有在 async 未执行完成返回结果时,才会挂起协程。若 async 已经有结果了,await() 则直接获取其结果并赋值给变量,此时不会挂起协程。
| 构建器 | 是否立即启动? | 串行?并行? | 是否阻塞当前线程? | 返回结果 |
|---|---|---|---|---|
| launch | 是 | 根据包裹的子协程类型而定 | 否 | Job对象 |
| async | 是 | 任务之间是并行 | 否 | Deferred,可以用await()方法获取结果 |
| runBlocking | 是 | 根据包裹的子协程类型而定 | 阻塞 | 子协程都执行完毕后才退出 |
| withContext | 不是 | 任务之间是串行 | 否 | 可以直接返回耗时任务结果,协程体最后一行内容 |
doAsync和async
- doAsync 的源码实现是基于Java的 Future 类进行异步处理和通过Handler进行线程切换 ,从而封装的一个扩展函数方便线程切换。
- doAsync 与 async 关系不大, doAsync并没有用到协程库中的内容。
- 可以通过 uiThread { } 切换回到主线程。
btn.setOnClickListener {
doAsync {
Log.e("TAG", " doAsync... [当前线程为:${Thread.currentThread().name}]")
uiThread {
Log.e("TAG", " uiThread.... [当前线程为:${Thread.currentThread().name}]")
}
}
}
GlobalScope.launch
GlobalScope.launch启动的协程默认运行在Default调度器上
关键说明
- 默认调度器:当使用
GlobalScope.launch { ... }且未显式指定调度器时,协程使用Dispatchers.Default作为其调度器。 Dispatchers.Default是一个共享的后台线程池,适用于CPU 密集型任务(如计算、数据处理等)。Dispatchers.Default不是主线程,不会阻塞 UI 线程,适合在后台执行耗时计算。
示例验证
GlobalScope.launch {
println("Thread: ${Thread.currentThread().name}")
// 输出线程名称通常包含 "DefaultDispatcher-worker-"
}
注意事项
- 不推荐在生产代码中使用 GlobalScope,因为它创建的协程生命周期与应用一致,容易导致内存泄漏或资源泄露。应优先使用作用域(如
lifecycleScope、viewModelScope或自定义CoroutineScope)来管理协程生命周期。 - 若需执行 IO 操作,应显式切换到
Dispatchers.IO;若需更新 UI,则应使用Dispatchers.Main。
总结
- 默认调度器:
Dispatchers.Default - 适用场景:后台 CPU 密集型任务
- 最佳实践:避免直接使用
GlobalScope,改用结构化并发的作用域管理协程。
lifecycleScope.launch
lifecycleScope.launch启动的协程默认运行在Main.immediate 调度器上
关键说明
- 默认调度器:
lifecycleScope是 Android Jetpack 提供的生命周期感知协程作用域,其底层协程上下文(CoroutineContext)默认包含SupervisorJob()+Dispatchers.Main.immediate。 -
Dispatchers.Main.immediate与Dispatchers.Main一样运行在主线程; Dispatchers.Main.immediate更高效:如果当前已在主线程,则立即执行;否则排队等待主线程空闲。- 生命周期绑定:协程会随
Activity/Fragment的onDestroy()自动取消,避免内存泄漏。
实践建议
- 若需执行耗时操作(如网络请求、文件读写),应显式切换调度器:
lifecycleScope.launch { val data = withContext(Dispatchers.IO) { fetchData() } textView.text = data // 切回主线程更新 UI } - 不要在
lifecycleScope.launch { }中直接执行阻塞主线程的操作,否则会导致 UI 卡顿。
总结:
lifecycleScope.launch默认运行在主线程(Dispatchers.Main.immediate),耗时任务需手动切换到Dispatchers.IO或Dispatchers.Default。
viewModelScope .launch
viewModelScope 默认运行在 Dispatchers.Main.immediate 调度器上,该调度器基于主线程(UI 线程),用于执行与 UI 相关的轻量级操作。
关键说明
- 默认调度器:
viewModelScope使用的是Dispatchers.Main.immediate,这是Dispatchers.Main的一个优化版本,在主线程上尽可能立即执行任务,避免不必要的线程切换 。 - 线程特性:虽然运行在主线程,但 不应执行耗时操作(如网络请求、数据库读写),否则会阻塞 UI,导致卡顿 。
- 切换线程:若需执行后台任务,应显式使用
withContext(Dispatchers.IO)或withContext(Dispatchers.Default)切换线程 。
注意事项
Dispatchers.Main.immediate与Dispatchers.Main行为基本一致,但在主线程已处于调度循环中时,会更高效地立即执行。- ViewModel 的生命周期长于 Activity/Fragment,因此
viewModelScope在 ViewModel 被清除(如 Activity 完全关闭)时会自动取消所有子协程,避免内存泄漏。
协程的启动模式
启动模式通过start参数传递。
- CoroutineStart.DEFAULT:协程创建后立即调度。在调度前如果协程被取消,进入取消响应状态。
- CoroutineStart.ATOMIC:协程创建后立即调度。协程执行到第一个挂起点之前不响应取消。立即调度不等于立即执行。
- CoroutineStart.LAZY:协程创建后不立即调度。只有主动调用协程的start、join或者await等函数时才会开始调度。如果调度前被取消,该协程进入异常结束状态。
- CoroutineStart.UNDISPATCHED:协程创建后立即在当前函数调用栈中执行,直到遇到第一个真正的挂起点。
@Test
fun `test start mode`() = runBlocking {
val job = async(context = Dispatchers.IO, start = CoroutineStart.UNDISPATCHED) {
println("thread:"+ Thread.currentThread().name)
}
}
//上面输出的线程名字是主线程,因为UNDISPATCHED会立即在当前线程中执行,而runBlocking是在主线程中
本文介绍了图形架构中的关键组件,包括窗口服务器、字体位图服务器、多媒体服务器及其在应用程序中的作用。详细讨论了图形设备(如屏幕和打印机)与图形上下文的概念,并提供了具体的类名示例,如 CFbsBitmapDevice 和 CWindowGc 的使用方法。

967

被折叠的 条评论
为什么被折叠?



