5.8 KiB
5.8 KiB
协程
目录
协程概念
什么是协程?
// 协程:轻量级线程
// 可以挂起和恢复
// 比线程更轻量,开销更小
// 创建协程
GlobalScope.launch {
delay(1000) // 挂起1秒
println("Hello from coroutine")
}
协程特点
- 轻量级:比线程更轻量,可以创建大量协程
- 挂起恢复:可以挂起和恢复执行
- 结构化并发:自动管理生命周期
- 简化异步代码:代码更简洁易读
协程与线程
协程 vs 线程
| 特性 | 协程 | 线程 |
|---|---|---|
| 创建开销 | 小 | 大 |
| 数量 | 可以创建大量 | 受系统限制 |
| 切换开销 | 小 | 大 |
| 阻塞 | 挂起(不阻塞线程) | 阻塞线程 |
| 适用场景 | 异步操作 | CPU 密集型任务 |
协程优势
// 协程:挂起不阻塞线程
suspend fun loadData() {
delay(1000) // 挂起,不阻塞线程
// 其他协程可以继续执行
}
// 线程:阻塞线程
Thread.sleep(1000) // 阻塞线程
协程作用域
作用域类型
1. GlobalScope
// GlobalScope:全局作用域
// 生命周期与应用一致
GlobalScope.launch {
delay(1000)
println("Hello")
}
2. CoroutineScope
// CoroutineScope:自定义作用域
val scope = CoroutineScope(Dispatchers.Main)
scope.launch {
delay(1000)
println("Hello")
}
scope.cancel() // 取消所有协程
3. lifecycleScope
// lifecycleScope:与 LifecycleOwner 绑定
class MainActivity : AppCompatActivity() {
fun loadData() {
lifecycleScope.launch {
val data = withContext(Dispatchers.IO) {
loadDataFromNetwork()
}
updateUI(data)
}
}
}
4. viewModelScope
// viewModelScope:与 ViewModel 绑定
class MyViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch {
val data = loadDataFromNetwork()
// 处理数据
}
}
}
协程上下文
Dispatchers
// Dispatchers.Main:主线程
launch(Dispatchers.Main) {
// 更新 UI
}
// Dispatchers.IO:IO 线程
launch(Dispatchers.IO) {
// 网络请求、文件操作
}
// Dispatchers.Default:默认线程池
launch(Dispatchers.Default) {
// CPU 密集型任务
}
// Dispatchers.Unconfined:不指定线程
launch(Dispatchers.Unconfined) {
// 在调用线程执行
}
切换上下文
// 切换上下文
launch(Dispatchers.Main) {
val data = withContext(Dispatchers.IO) {
// 在 IO 线程执行
loadDataFromNetwork()
}
// 回到主线程
updateUI(data)
}
协程取消
取消协程
// 取消协程
val job = launch {
delay(10000)
println("Done")
}
job.cancel() // 取消协程
// 检查是否取消
launch {
while (isActive) {
// 执行任务
delay(100)
}
}
取消检查
// 检查取消状态
suspend fun doWork() {
for (i in 1..1000) {
ensureActive() // 检查是否取消
// 执行任务
}
}
取消异常
// 取消时抛出 CancellationException
try {
delay(10000)
} catch (e: CancellationException) {
// 处理取消
throw e // 必须重新抛出
}
协程异常处理
try-catch
// 使用 try-catch
launch {
try {
val data = loadData()
} catch (e: Exception) {
// 处理异常
}
}
CoroutineExceptionHandler
// 使用 CoroutineExceptionHandler
val handler = CoroutineExceptionHandler { _, exception ->
Log.e("Coroutine", "Exception: $exception")
}
launch(handler) {
throw Exception("Error")
}
SupervisorJob
// SupervisorJob:子协程异常不影响其他协程
val supervisor = SupervisorJob()
val scope = CoroutineScope(Dispatchers.Main + supervisor)
scope.launch {
// 子协程1
}
scope.launch {
// 子协程2:异常不影响子协程1
throw Exception("Error")
}
协程最佳实践
1. 使用合适的作用域
// ✅ 推荐:使用 lifecycleScope 或 viewModelScope
lifecycleScope.launch {
// 协程代码
}
// ❌ 不推荐:使用 GlobalScope
GlobalScope.launch {
// 可能导致内存泄漏
}
2. 及时取消协程
// 在 onDestroy 中取消
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
3. 使用 suspend 函数
// ✅ 推荐:使用 suspend 函数
suspend fun loadData(): String {
return withContext(Dispatchers.IO) {
// 网络请求
}
}
// ❌ 不推荐:在协程中直接使用回调
面试常见问题
Q1: 什么是协程?
答案:
- 轻量级线程
- 可以挂起和恢复
- 比线程更轻量,开销更小
- 简化异步代码
Q2: 协程和线程的区别?
答案:
- 协程:轻量级,可以创建大量,挂起不阻塞线程
- 线程:重量级,受系统限制,阻塞线程
Q3: 协程作用域?
答案:
- GlobalScope:全局作用域
- CoroutineScope:自定义作用域
- lifecycleScope:与 LifecycleOwner 绑定
- viewModelScope:与 ViewModel 绑定
Q4: 如何取消协程?
答案:
- 调用
job.cancel() - 检查
isActive - 使用
ensureActive()
Q5: 协程异常处理?
答案:
- 使用 try-catch
- 使用 CoroutineExceptionHandler
- 使用 SupervisorJob
最后更新:2024年