# 协程

## 目录
- [协程概念](#协程概念)
- [协程与线程](#协程与线程)
- [协程作用域](#协程作用域)
- [协程上下文](#协程上下文)
- [协程取消](#协程取消)
- [协程异常处理](#协程异常处理)
- [协程最佳实践](#协程最佳实践)
- [面试常见问题](#面试常见问题)

---

## 协程概念

### 什么是协程？

```kotlin
// 协程：轻量级线程
// 可以挂起和恢复
// 比线程更轻量，开销更小

// 创建协程
GlobalScope.launch {
    delay(1000) // 挂起1秒
    println("Hello from coroutine")
}
```

### 协程特点

1. **轻量级**：比线程更轻量，可以创建大量协程
2. **挂起恢复**：可以挂起和恢复执行
3. **结构化并发**：自动管理生命周期
4. **简化异步代码**：代码更简洁易读

---

## 协程与线程

### 协程 vs 线程

| 特性 | 协程 | 线程 |
|------|------|------|
| 创建开销 | 小 | 大 |
| 数量 | 可以创建大量 | 受系统限制 |
| 切换开销 | 小 | 大 |
| 阻塞 | 挂起（不阻塞线程） | 阻塞线程 |
| 适用场景 | 异步操作 | CPU 密集型任务 |

### 协程优势

```kotlin
// 协程：挂起不阻塞线程
suspend fun loadData() {
    delay(1000) // 挂起，不阻塞线程
    // 其他协程可以继续执行
}

// 线程：阻塞线程
Thread.sleep(1000) // 阻塞线程
```

---

## 协程作用域

### 作用域类型

#### 1. GlobalScope

```kotlin
// GlobalScope：全局作用域
// 生命周期与应用一致
GlobalScope.launch {
    delay(1000)
    println("Hello")
}
```

#### 2. CoroutineScope

```kotlin
// CoroutineScope：自定义作用域
val scope = CoroutineScope(Dispatchers.Main)
scope.launch {
    delay(1000)
    println("Hello")
}
scope.cancel() // 取消所有协程
```

#### 3. lifecycleScope

```kotlin
// lifecycleScope：与 LifecycleOwner 绑定
class MainActivity : AppCompatActivity() {
    fun loadData() {
        lifecycleScope.launch {
            val data = withContext(Dispatchers.IO) {
                loadDataFromNetwork()
            }
            updateUI(data)
        }
    }
}
```

#### 4. viewModelScope

```kotlin
// viewModelScope：与 ViewModel 绑定
class MyViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {
            val data = loadDataFromNetwork()
            // 处理数据
        }
    }
}
```

---

## 协程上下文

### Dispatchers

```kotlin
// Dispatchers.Main：主线程
launch(Dispatchers.Main) {
    // 更新 UI
}

// Dispatchers.IO：IO 线程
launch(Dispatchers.IO) {
    // 网络请求、文件操作
}

// Dispatchers.Default：默认线程池
launch(Dispatchers.Default) {
    // CPU 密集型任务
}

// Dispatchers.Unconfined：不指定线程
launch(Dispatchers.Unconfined) {
    // 在调用线程执行
}
```

### 切换上下文

```kotlin
// 切换上下文
launch(Dispatchers.Main) {
    val data = withContext(Dispatchers.IO) {
        // 在 IO 线程执行
        loadDataFromNetwork()
    }
    // 回到主线程
    updateUI(data)
}
```

---

## 协程取消

### 取消协程

```kotlin
// 取消协程
val job = launch {
    delay(10000)
    println("Done")
}
job.cancel() // 取消协程

// 检查是否取消
launch {
    while (isActive) {
        // 执行任务
        delay(100)
    }
}
```

### 取消检查

```kotlin
// 检查取消状态
suspend fun doWork() {
    for (i in 1..1000) {
        ensureActive() // 检查是否取消
        // 执行任务
    }
}
```

### 取消异常

```kotlin
// 取消时抛出 CancellationException
try {
    delay(10000)
} catch (e: CancellationException) {
    // 处理取消
    throw e // 必须重新抛出
}
```

---

## 协程异常处理

### try-catch

```kotlin
// 使用 try-catch
launch {
    try {
        val data = loadData()
    } catch (e: Exception) {
        // 处理异常
    }
}
```

### CoroutineExceptionHandler

```kotlin
// 使用 CoroutineExceptionHandler
val handler = CoroutineExceptionHandler { _, exception ->
    Log.e("Coroutine", "Exception: $exception")
}

launch(handler) {
    throw Exception("Error")
}
```

### SupervisorJob

```kotlin
// SupervisorJob：子协程异常不影响其他协程
val supervisor = SupervisorJob()
val scope = CoroutineScope(Dispatchers.Main + supervisor)

scope.launch {
    // 子协程1
}

scope.launch {
    // 子协程2：异常不影响子协程1
    throw Exception("Error")
}
```

---

## 协程最佳实践

### 1. 使用合适的作用域

```kotlin
// ✅ 推荐：使用 lifecycleScope 或 viewModelScope
lifecycleScope.launch {
    // 协程代码
}

// ❌ 不推荐：使用 GlobalScope
GlobalScope.launch {
    // 可能导致内存泄漏
}
```

### 2. 及时取消协程

```kotlin
// 在 onDestroy 中取消
override fun onDestroy() {
    super.onDestroy()
    job.cancel()
}
```

### 3. 使用 suspend 函数

```kotlin
// ✅ 推荐：使用 suspend 函数
suspend fun loadData(): String {
    return withContext(Dispatchers.IO) {
        // 网络请求
    }
}

// ❌ 不推荐：在协程中直接使用回调
```

---

## 面试常见问题

### Q1: 什么是协程？

**答案：**
- 轻量级线程
- 可以挂起和恢复
- 比线程更轻量，开销更小
- 简化异步代码

### Q2: 协程和线程的区别？

**答案：**
- **协程**：轻量级，可以创建大量，挂起不阻塞线程
- **线程**：重量级，受系统限制，阻塞线程

### Q3: 协程作用域？

**答案：**
1. **GlobalScope**：全局作用域
2. **CoroutineScope**：自定义作用域
3. **lifecycleScope**：与 LifecycleOwner 绑定
4. **viewModelScope**：与 ViewModel 绑定

### Q4: 如何取消协程？

**答案：**
1. 调用 `job.cancel()`
2. 检查 `isActive`
3. 使用 `ensureActive()`

### Q5: 协程异常处理？

**答案：**
1. 使用 try-catch
2. 使用 CoroutineExceptionHandler
3. 使用 SupervisorJob

---

*最后更新：2024年*
