6-1-1 协程作用域-详解

32 阅读4分钟

Kotlin 协程作用域详解

协程作用域是 Kotlin 协程中管理并发和生命周期的核心概念。它定义了协程的执行范围和生命周期,是结构化并发的基础。

1. 协程作用域的基本概念

1.1 什么是协程作用域

// 作用域接口定义
public interface CoroutineScope {
    val coroutineContext: CoroutineContext
}

// 所有作用域都提供协程上下文
// 作用域的主要职责:
// 1. 管理协程的生命周期
// 2. 提供取消机制
// 3. 传播异常
// 4. 确保结构化并发

2. 内置作用域类型

2.1 GlobalScope - 全局作用域

// GlobalScope 是应用级别的全局作用域
// ⚠️ 警告:谨慎使用,容易造成内存泄漏

fun useGlobalScope() {
    // 启动一个全局协程
    val job = GlobalScope.launch {
        repeat(10) {
            delay(1000)
            println("GlobalScope task: $it")
        }
    }
    
    // 问题:需要手动管理生命周期
    // job.cancel() // 必须手动取消
}

// GlobalScope 的特点:
// - 生命周期与应用程序相同
// - 不会自动取消
// - 没有父协程
// - 使用 Dispatchers.Default 作为默认调度器

// GlobalScope 的正确使用场景
object GlobalTasks {
    // 场景1:应用级别的后台任务
    fun startHeartbeat() {
        GlobalScope.launch {
            while (isActive) {
                sendHeartbeat()
                delay(30000) // 每30秒一次
            }
        }
    }
    
    // 场景2:日志收集
    fun logEvent(event: String) {
        GlobalScope.launch(Dispatchers.IO) {
            // 异步记录日志,不阻塞主线程
            saveToLogFile(event)
        }
    }
    
    // 场景3:缓存清理
    fun scheduleCacheCleanup() {
        GlobalScope.launch {
            while (true) {
                delay(3600000) // 每小时清理一次
                cleanExpiredCache()
            }
        }
    }
}

// GlobalScope 的错误使用示例
class UserRepository {
    // ❌ 错误:可能造成内存泄漏
    fun loadUserData(userId: String) {
        GlobalScope.launch {
            // 长时间运行的任务
            val data = fetchUserData(userId)
            // 如果调用方(如Activity)已经销毁,这里可能崩溃
            updateUI(data)
        }
    }
    
    // ✅ 正确:使用接收的作用域
    fun loadUserData(scope: CoroutineScope, userId: String) {
        scope.launch {
            val data = fetchUserData(userId)
            updateUI(data)
        }
    }
}

2.2 自定义作用域

// 创建自定义作用域
class MyCustomScope : CoroutineScope {
    private val job = SupervisorJob()
    private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
        println("Caught exception: $throwable")
    }
    
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job + exceptionHandler + CoroutineName("MyScope")
    
    fun startTasks() {
        // 在自定义作用域中启动协程
        launch {
            // 任务1
        }
        
        launch {
            // 任务2
        }
    }
    
    fun cleanup() {
        // 取消作用域中的所有协程
        job.cancel()
    }
}

// 工厂方式创建作用域
fun createCustomScope(): CoroutineScope {
    return CoroutineScope(
        Dispatchers.IO + 
        SupervisorJob() + 
        CoroutineExceptionHandler { _, e ->
            logError("Scope error: $e")
        } +
        CoroutineName("CustomBackgroundScope")
    )
}

3. Android 中的生命周期感知作用域

3.1 lifecycleScope

// lifecycleScope 与 Android 组件生命周期绑定
class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // lifecycleScope 在 Activity 销毁时自动取消
        
        // 1. 基本使用
        lifecycleScope.launch {
            // 在主线程执行(默认 Dispatchers.Main.immediate)
            val user = withContext(Dispatchers.IO) {
                loadUserData()
            }
            updateUI(user)
        }
        
        // 2. 使用不同的调度器
        lifecycleScope.launch(Dispatchers.IO) {
            // 在IO线程执行
            val data = fetchData()
            withContext(Dispatchers.Main) {
                updateUI(data)
            }
        }
        
        // 3. 生命周期感知的启动方式
        lifecycleScope.launchWhenCreated {
            // 只在 CREATED 状态及以上执行
            // 当生命周期低于 CREATED 时自动挂起
            loadInitialData()
        }
        
        lifecycleScope.launchWhenStarted {
            // 只在 STARTED 状态及以上执行
            // 适合UI更新操作
            observeDataUpdates()
        }
        
        lifecycleScope.launchWhenResumed {
            // 只在 RESUMED 状态执行
            // 适合需要用户交互的操作
            startAnimation()
        }
        
        // 4. 收集 Flow
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // 推荐的方式:只在 STARTED 状态收集
                viewModel.dataFlow.collect { data ->
                    updateUI(data)
                }
            }
        }
    }
}

// lifecycleScope 在 Fragment 中的使用
class MyFragment : Fragment() {
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 使用 viewLifecycleOwner 而不是 lifecycleOwner
        // 避免 Fragment 视图重建时的生命周期问题
        viewLifecycleOwner.lifecycleScope.launch {
            // 安全地更新UI
            loadFragmentData()
        }
        
        // 收集 Flow 的最佳实践
        viewLifecycleOwner.lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect { state ->
                    when (state) {
                        is UiState.Loading -> showLoading()
                        is UiState.Success -> showData(state.data)
                        is UiState.Error -> showError(state.message)
                    }
                }
            }
        }
    }
}

3.2 viewModelScope

// viewModelScope 与 ViewModel 生命周期绑定
class MyViewModel : ViewModel() {
    
    private val _uiState = MutableStateFlow<UiState>(UiState.Idle)
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()
    
    // 1. 基本数据加载
    fun loadData() {
        viewModelScope.launch {
            _uiState.value = UiState.Loading
            try {
                val data = withContext(Dispatchers.IO) {
                    repository.fetchData()
                }
                _uiState.value = UiState.Success(data)
            } catch (e: Exception) {
                _uiState.value = UiState.Error(e.message ?: "Unknown error")
            }
        }
    }
    
    // 2. 并行任务
    fun loadMultipleData() {
        viewModelScope.launch {
            val deferredUser = async { repository.getUser() }
            val deferredPosts = async { repository.getPosts() }
            val deferredFriends = async { repository.getFriends() }
            
            try {
                val user = deferredUser.await()
                val posts = deferredPosts.await()
                val friends = deferredFriends.await()
                
                _uiState.value = UiState.Success(CombinedData(user, posts, friends))
            } catch (e: Exception) {
                // 如果任何一个失败,所有都会取消
                _uiState.value = UiState.Error(e.message ?: "Failed to load data")
            }
        }
    }
    
    // 3. 使用 supervisorScope 处理独立任务
    fun startIndependentTasks() {
        viewModelScope.launch {
            supervisorScope {
                // 任务1:独立运行,失败不影响其他任务
                launch {
                    try {
                        syncUserData()
                    } catch (e: Exception) {
                        logError("Sync failed: $e")
                    }
                }
                
                // 任务2:独立运行
                launch {
                    try {
                        updateCache()
                    } catch (e: Exception) {
                        logError("Cache update failed: $e")
                    }
                }
            }
        }
    }
    
    // 4. 取消特定任务
    private var dataJob: Job? = null
    
    fun loadDataWithCancel() {
        // 取消之前的任务
        dataJob?.cancel()
        
        // 启动新任务
        dataJob = viewModelScope.launch {
            val data = repository.fetchData()
            _uiState.value = UiState.Success(data)
        }
    }
    
    // 5. 超时控制
    fun loadDataWithTimeout() {
        viewModelScope.launch {
            try {
                val data = withTimeout(5000) { // 5秒超时
                    repository.fetchData()
                }
                _uiState.value = UiState.Success(data)
            } catch (e: TimeoutCancellationException) {
                _uiState.value = UiState.Error("Request timeout")
            } catch (e: Exception) {
                _uiState.value = UiState.Error(e.message ?: "Error")
            }
        }
    }
}

4. 作用域构建器

4.1 coroutineScope 构建器

// coroutineScope:结构化并发的基础
suspend fun processUserData(userId: String): UserData = coroutineScope {
    // coroutineScope 创建一个新的作用域
    // 特点:
    // 1. 继承父协程的上下文
    // 2. 等待所有子协程完成
    // 3. 任何一个子协程失败,所有子协程都会取消
    
    // 并发执行多个任务
    val userDeferred = async { fetchUser(userId) }
    val profileDeferred = async { fetchProfile(userId) }
    val postsDeferred = async { fetchPosts(userId) }
    
    try {
        // 等待所有任务完成
        val user = userDeferred.await()
        val profile = profileDeferred.await()
        val posts = postsDeferred.await()
        
        UserData(user, profile, posts)
    } catch (e: Exception) {
        // 任何一个失败,都会取消所有任务
        throw ProcessUserException("Failed to process user data", e)
    }
}

// 嵌套使用
suspend fun complexOperation(): Result = coroutineScope {
    // 第一层:获取基础数据
    val baseData = async { fetchBaseData() }.await()
    
    // 第二层:基于基础数据获取更多数据
    coroutineScope {
        val detailsDeferred = async { fetchDetails(baseData.id) }
        val relatedDeferred = async { fetchRelated(baseData.id) }
        
        val details = detailsDeferred.await()
        val related = relatedDeferred.await()
        
        Result(baseData, details, related)
    }
}

4.2 supervisorScope 构建器

// supervisorScope:允许子协程独立失败
suspend fun startIndependentTasks() = supervisorScope {
    // supervisorScope 特点:
    // 1. 使用 SupervisorJob
    // 2. 子协程失败不会影响其他子协程
    // 3. 不会自动取消其他子协程
    
    // 任务1:即使失败也不影响其他任务
    val task1 = launch(CoroutineExceptionHandler { _, e ->
        println("Task1 failed: $e")
    }) {
        riskyOperation1()
    }
    
    // 任务2:独立运行
    val task2 = launch {
        safeOperation()
    }
    
    // 任务3:也独立运行
    val task3 = launch {
        anotherRiskyOperation()
    }
    
    // 等待所有任务完成(或失败)
    joinAll(task1, task2, task3)
}

// 实际应用:批量处理,允许部分失败
suspend fun batchProcess(items: List<Item>): BatchResult = supervisorScope {
    val results = mutableListOf<ItemResult>()
    val errors = mutableListOf<Throwable>()
    
    // 为每个项目启动独立协程
    items.map { item ->
        launch(CoroutineExceptionHandler { _, e ->
            errors.add(e)
        }) {
            val result = processItem(item)
            results.add(result)
        }
    }.joinAll() // 等待所有协程完成
    
    BatchResult(results, errors)
}

4.3 withContext 构建器

// withContext:切换上下文
suspend fun loadData(): Data {
    // withContext 不是创建新的作用域,而是切换上下文
    
    // 1. 切换到IO线程执行耗时操作
    val rawData = withContext(Dispatchers.IO) {
        fetchFromNetwork()
    }
    
    // 2. 在Default线程池处理数据
    val processedData = withContext(Dispatchers.Default) {
        processData(rawData)
    }
    
    // 3. 返回主线程更新状态
    withContext(Dispatchers.Main) {
        updateLoadingState(false)
    }
    
    return processedData
}

// withContext 的嵌套使用
suspend fun complexWorkflow() {
    // 外层:IO操作
    val data = withContext(Dispatchers.IO) {
        val raw = fetchData()
        
        // 内层:在IO线程中继续执行CPU密集型操作
        withContext(Dispatchers.Default) {
            processRawData(raw)
        }
    }
    
    // 回到调用者的调度器(可能是Main)
    displayData(data)
}

5. 作用域与结构化并发

5.1 结构化并发原理

// 结构化并发:父协程的生命周期包含子协程
fun demonstrateStructuredConcurrency() {
    val parentScope = CoroutineScope(Dispatchers.Default)
    
    parentScope.launch {
        println("Parent started")
        
        // 子协程1
        launch {
            delay(1000)
            println("Child 1 completed")
        }
        
        // 子协程2
        launch {
            delay(2000)
            println("Child 2 completed")
        }
        
        // 等待所有子协程完成
        delay(1500)
        
        // 取消父协程会取消所有子协程
        // 但如果子协程已经完成,则不受影响
    }
    
    // 取消整个作用域
    Thread.sleep(500)
    parentScope.cancel() // 会取消父协程和所有子协程
}

// 非结构化并发 vs 结构化并发
class TaskManager {
    // ❌ 非结构化:难以管理
    private val jobs = mutableListOf<Job>()
    
    fun startUnstructuredTasks() {
        repeat(5) { i ->
            val job = GlobalScope.launch {
                delay(1000 * i)
                println("Task $i completed")
            }
            jobs.add(job)
        }
        
        // 需要手动管理所有job
    }
    
    fun cancelAllUnstructured() {
        jobs.forEach { it.cancel() }
        jobs.clear()
    }
    
    // ✅ 结构化:易于管理
    private val structuredScope = CoroutineScope(Dispatchers.Default)
    
    fun startStructuredTasks() {
        structuredScope.launch {
            repeat(5) { i ->
                launch {
                    delay(1000 * i)
                    println("Structured task $i completed")
                }
            }
        }
    }
    
    fun cancelAllStructured() {
        structuredScope.cancel() // 一键取消所有
    }
}

5.2 作用域层次与取消传播

// 作用域层次和取消传播
fun demonstrateCancellationPropagation() {
    val scope = CoroutineScope(Dispatchers.Default + CoroutineName("RootScope"))
    
    val parentJob = scope.launch(CoroutineName("Parent")) {
        println("${coroutineContext[CoroutineName]} started")
        
        // 子协程1
        val child1 = launch(CoroutineName("Child1")) {
            try {
                repeat(10) { i ->
                    delay(500)
                    println("${coroutineContext[CoroutineName]} iteration $i")
                }
            } finally {
                println("${coroutineContext[CoroutineName]} cancelled, cleaning up...")
                delay(100) // 清理工作
                println("${coroutineContext[CoroutineName]} cleanup completed")
            }
        }
        
        // 子协程2
        val child2 = launch(CoroutineName("Child2")) {
            try {
                repeat(5) { i ->
                    delay(1000)
                    println("${coroutineContext[CoroutineName]} iteration $i")
                }
            } finally {
                println("${coroutineContext[CoroutineName]} cancelled")
            }
        }
        
        delay(1200)
        println("${coroutineContext[CoroutineName]} cancelling children")
        child1.cancel() // 只取消child1,child2继续运行
        
        child1.join() // 等待child1完成取消
        child2.join() // 等待child2自然完成
    }
    
    Thread.sleep(3000)
    println("Cancelling entire scope")
    scope.cancel() // 取消整个作用域
}

6. 作用域的最佳实践

6.1 创建自定义作用域的指导原则

// 自定义作用域工厂
object CoroutineScopes {
    
    // 场景1:后台任务作用域
    val backgroundScope: CoroutineScope by lazy {
        CoroutineScope(
            Dispatchers.IO +
            SupervisorJob() +
            CoroutineExceptionHandler { _, e ->
                Log.e("BackgroundScope", "Unhandled exception", e)
            } +
            CoroutineName("BackgroundScope")
        )
    }
    
    // 场景2:网络请求作用域
    val networkScope: CoroutineScope by lazy {
        CoroutineScope(
            Dispatchers.IO +
            SupervisorJob() +
            CoroutineExceptionHandler { _, e ->
                Analytics.logError("NetworkError", e)
            } +
            CoroutineName("NetworkScope") +
            // 添加超时配置
            object : AbstractCoroutineContextElement(Key), CompletableJob {
                override val key: CoroutineContext.Key<*> = Key
                override fun complete() = Unit
                override fun completeExceptionally(exception: Throwable) = Unit
                override fun getOnJoin(): SelectClause0? = null
                override fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle 
                    = NonDisposableHandle
                override fun isActive(): Boolean = true
                override fun isCancelled(): Boolean = false
                override fun isCompleted(): Boolean = false
                override fun join() { throw UnsupportedOperationException() }
                override suspend fun join() { throw UnsupportedOperationException() }
                override fun start(): Boolean = true
                
                companion object Key : CoroutineContext.Key<*>
            }
        )
    }
    
    // 场景3:数据库操作作用域
    val databaseScope: CoroutineScope by lazy {
        CoroutineScope(
            Dispatchers.IO +
            Job() + // 使用普通Job,一个失败全部取消
            CoroutineExceptionHandler { _, e ->
                Log.e("DatabaseScope", "Database operation failed", e)
            } +
            CoroutineName("DatabaseScope")
        )
    }
    
    // 清理所有作用域
    fun cleanup() {
        backgroundScope.cancel()
        networkScope.cancel()
        databaseScope.cancel()
    }
}

// 封装业务特定的作用域
class FeatureScope(
    private val parentScope: CoroutineScope,
    private val featureName: String
) : CoroutineScope {
    
    private val featureJob = SupervisorJob(parentScope.coroutineContext[Job])
    
    override val coroutineContext: CoroutineContext
        get() = parentScope.coroutineContext + featureJob + CoroutineName(featureName)
    
    // 启动特定类型的任务
    fun launchDataTask(block: suspend CoroutineScope.() -> Unit): Job {
        return launch(Dispatchers.IO) {
            try {
                block()
            } catch (e: Exception) {
                handleFeatureError(e)
                throw e
            }
        }
    }
    
    fun launchUITask(block: suspend CoroutineScope.() -> Unit): Job {
        return launch(Dispatchers.Main) {
            try {
                block()
            } catch (e: Exception) {
                showErrorToUser(e)
            }
        }
    }
    
    // 取消此功能的所有协程
    fun cancelFeature() {
        featureJob.cancel()
    }
    
    private fun handleFeatureError(e: Exception) {
        // 特定的错误处理逻辑
    }
    
    private fun showErrorToUser(e: Exception) {
        // 显示错误给用户
    }
}

6.2 避免常见的作用域错误

// 常见错误和解决方案
class ScopeAntiPatterns {
    
    // ❌ 错误1:泄漏的作用域
    object LeakyScope {
        private val scope = CoroutineScope(Dispatchers.IO)
        
        fun processData(data: String) {
            scope.launch {
                // 长时间运行的任务
                heavyProcessing(data)
                // 问题:scope永远不会被取消
            }
        }
    }
    
    // ✅ 解决方案1:提供清理方法
    object CleanScope {
        private val scope = CoroutineScope(Dispatchers.IO)
        
        fun processData(data: String): Job {
            return scope.launch {
                heavyProcessing(data)
            }
        }
        
        fun cleanup() {
            scope.cancel()
        }
    }
    
    // ❌ 错误2:在错误的线程更新UI
    class WrongThreadUI {
        fun updateData() {
            GlobalScope.launch(Dispatchers.IO) {
                val data = fetchData()
                // 错误:在IO线程更新UI
                updateUI(data) // 可能崩溃
            }
        }
    }
    
    // ✅ 解决方案2:正确切换线程
    class CorrectThreadUI {
        fun updateData() {
            // 假设这是Activity的方法
            lifecycleScope.launch {
                val data = withContext(Dispatchers.IO) {
                    fetchData()
                }
                // 正确:自动回到主线程
                updateUI(data)
            }
        }
    }
    
    // ❌ 错误3:忽视取消
    class IgnoreCancellation {
        suspend fun longRunningTask() {
            // 问题:不检查协程是否活跃
            repeat(100) {
                heavyComputation() // 即使被取消也会继续执行
                // 应该检查 isActive
            }
        }
    }
    
    // ✅ 解决方案3:协作式取消
    class CooperativeCancellation {
        suspend fun longRunningTask() {
            ensureActive() // 先检查是否活跃
            
            repeat(100) { i ->
                ensureActive() // 定期检查
                heavyComputation(i)
                
                // 或者使用 yield() 让出执行权并检查取消
                yield()
            }
        }
        
        suspend fun ioOperationWithCancellation() {
            // 对于阻塞操作,使用可取消的变体
            withContext(Dispatchers.IO) {
                // 使用可取消的阻塞调用
                runInterruptible { 
                    Thread.sleep(1000) // 现在可以被取消了
                }
            }
        }
    }
    
    // ❌ 错误4:异常处理不当
    class BadExceptionHandling {
        fun startTask() {
            GlobalScope.launch {
                try {
                    riskyOperation()
                } catch (e: Exception) {
                    // 只处理了直接异常
                    println("Caught: $e")
                }
            }
        }
    }
    
    // ✅ 解决方案4:全面异常处理
    class GoodExceptionHandling {
        private val scope = CoroutineScope(
            Dispatchers.Default +
            SupervisorJob() +
            CoroutineExceptionHandler { _, e ->
                // 处理未捕获的异常
                Log.e("Coroutine", "Uncaught exception", e)
            }
        )
        
        fun startTask() {
            scope.launch {
                try {
                    riskyOperation()
                } catch (e: SpecificException) {
                    // 处理特定异常
                    handleSpecificError(e)
                } catch (e: Exception) {
                    // 处理其他异常
                    handleGenericError(e)
                } finally {
                    // 清理资源
                    cleanup()
                }
            }
        }
    }
}

6.3 作用域的性能优化

// 作用域的性能考虑
class ScopePerformance {
    
    // 1. 重用作用域而不是频繁创建
    object ScopeReuse {
        // ❌ 不好:频繁创建新作用域
        fun processItemBad(item: Item) {
            val scope = CoroutineScope(Dispatchers.IO) // 每次都创建
            scope.launch {
                process(item)
            }
            // 需要手动取消和清理
        }
        
        // ✅ 好:重用作用域
        private val processingScope = CoroutineScope(
            Dispatchers.IO + 
            SupervisorJob() +
            CoroutineName("ProcessingScope")
        )
        
        fun processItemGood(item: Item): Job {
            return processingScope.launch {
                process(item)
            }
        }
        
        fun cleanup() {
            processingScope.cancel()
        }
    }
    
    // 2. 合理配置调度器
    object DispatcherConfiguration {
        // 根据任务类型选择合适的调度器
        
        // IO密集型任务
        val ioScope = CoroutineScope(Dispatchers.IO.limitedParallelism(64))
        
        // CPU密集型任务
        val cpuScope = CoroutineScope(Dispatchers.Default.limitedParallelism(4))
        
        // 单线程任务(需要顺序执行)
        val singleThreadScope = CoroutineScope(newSingleThreadContext("SingleThread"))
        
        // 无限制任务(谨慎使用)
        val unlimitedScope = CoroutineScope(Dispatchers.IO.limitedParallelism(Int.MAX_VALUE))
    }
    
    // 3. 监控作用域状态
    class MonitoredScope(
        name: String,
        parentScope: CoroutineScope
    ) : CoroutineScope {
        
        override val coroutineContext: CoroutineContext
        
        private val activeJobs = ConcurrentHashMap<Job, String>()
        private val jobCounter = AtomicInteger(0)
        
        init {
            val job = SupervisorJob(parentScope.coroutineContext[Job])
            coroutineContext = parentScope.coroutineContext + job + CoroutineName(name)
        }
        
        override fun launch(
            context: CoroutineContext = EmptyCoroutineContext,
            start: CoroutineStart = CoroutineStart.DEFAULT,
            block: suspend CoroutineScope.() -> Unit
        ): Job {
            val jobId = "Job-${jobCounter.incrementAndGet()}"
            
            val job = super.launch(context, start) {
                activeJobs[coroutineContext[Job]!!] = jobId
                try {
                    block()
                } finally {
                    activeJobs.remove(coroutineContext[Job])
                }
            }
            
            return job
        }
        
        fun getActiveJobCount(): Int = activeJobs.size
        
        fun getActiveJobIds(): List<String> = activeJobs.values.toList()
        
        fun cancelAll() {
            coroutineContext[Job]?.cancel()
            activeJobs.clear()
        }
    }
}

7. 实际项目中的应用模式

7.1 MVVM 架构中的作用域使用

// 完整的 MVVM 协程作用域模式
class UserViewModel(
    private val userRepository: UserRepository,
    private val analytics: Analytics
) : ViewModel() {
    
    // UI状态
    private val _uiState = MutableStateFlow<UserUiState>(UserUiState.Idle)
    val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
    
    // 专门的协程用于收集Flow
    private var collectionJob: Job? = null
    
    // 初始化
    init {
        observeUserUpdates()
    }
    
    // 方法1:加载用户数据
    fun loadUser(userId: String) {
        viewModelScope.launch {
            _uiState.value = UserUiState.Loading
            
            try {
                // 使用 withTimeout 防止长时间挂起
                val user = withTimeout(10000) {
                    userRepository.getUser(userId)
                }
                
                _uiState.value = UserUiState.Success(user)
                analytics.logEvent("user_loaded", mapOf("userId" to userId))
                
            } catch (e: TimeoutCancellationException) {
                _uiState.value = UserUiState.Error("Request timeout")
                analytics.logError("user_load_timeout", e)
                
            } catch (e: Exception) {
                _uiState.value = UserUiState.Error(e.message ?: "Unknown error")
                analytics.logError("user_load_failed", e)
            }
        }
    }
    
    // 方法2:并发加载用户详情
    fun loadUserDetails(userId: String) {
        viewModelScope.launch {
            _uiState.value = UserUiState.Loading
            
            supervisorScope {
                try {
                    val userDeferred = async { userRepository.getUser(userId) }
                    val postsDeferred = async { userRepository.getUserPosts(userId) }
                    val friendsDeferred = async { userRepository.getUserFriends(userId) }
                    
                    val user = userDeferred.await()
                    val posts = postsDeferred.await()
                    val friends = friendsDeferred.await()
                    
                    _uiState.value = UserUiState.DetailsSuccess(
                        user = user,
                        posts = posts,
                        friends = friends
                    )
                    
                } catch (e: Exception) {
                    _uiState.value = UserUiState.Error("Failed to load details")
                }
            }
        }
    }
    
    // 方法3:观察数据更新
    private fun observeUserUpdates() {
        // 取消之前的观察
        collectionJob?.cancel()
        
        collectionJob = viewModelScope.launch {
            userRepository.userUpdates
                .filterNotNull()
                .distinctUntilChanged()
                .collect { user ->
                    _uiState.value = UserUiState.Success(user)
                }
        }
    }
    
    // 方法4:执行后台任务
    fun syncUserData() {
        viewModelScope.launch {
            // 使用不同的上下文执行后台任务
            withContext(Dispatchers.IO + CoroutineName("UserSync")) {
                userRepository.syncWithBackend()
            }
        }
    }
    
    // 清理资源
    override fun onCleared() {
        super.onCleared()
        collectionJob?.cancel()
    }
}

sealed class UserUiState {
    object Idle : UserUiState()
    object Loading : UserUiState()
    data class Success(val user: User) : UserUiState()
    data class DetailsSuccess(val user: User, val posts: List<Post>, val friends: List<User>) : UserUiState()
    data class Error(val message: String) : UserUiState()
}

7.2 在 Repository 中使用作用域

class UserRepository(
    private val api: UserApi,
    private val database: UserDatabase,
    private val ioScope: CoroutineScope // 注入作用域
) {
    
    // 方法1:获取用户,带缓存
    suspend fun getUser(userId: String): User {
        // 先尝试从数据库获取
        val cachedUser = withContext(Dispatchers.IO) {
            database.userDao().getById(userId)
        }
        
        if (cachedUser != null) {
            // 异步更新缓存
            ioScope.launch {
                try {
                    val freshUser = api.getUser(userId)
                    database.userDao().insert(freshUser)
                } catch (e: Exception) {
                    // 静默失败,使用缓存数据
                    Log.w("UserRepository", "Failed to refresh user cache", e)
                }
            }
            return cachedUser
        }
        
        // 缓存未命中,从网络获取
        return withContext(Dispatchers.IO) {
            val user = api.getUser(userId)
            database.userDao().insert(user)
            user
        }
    }
    
    // 方法2:批量处理
    suspend fun syncAllUsers(): SyncResult = supervisorScope {
        val results = mutableListOf<User>()
        val errors = mutableListOf<Throwable>()
        
        val userIds = database.userDao().getAllIds()
        
        // 并发同步所有用户,但限制并发数
        val semaphore = Semaphore(5) // 最多5个并发请求
        
        userIds.map { userId ->
            launch {
                semaphore.withPermit {
                    try {
                        val user = api.getUser(userId)
                        database.userDao().insert(user)
                        results.add(user)
                    } catch (e: Exception) {
                        errors.add(e)
                    }
                }
            }
        }.joinAll()
        
        SyncResult(results, errors)
    }
    
    // 方法3:分页加载
    fun getUsersPaginated(pageSize: Int): Flow<PagingData<User>> {
        return Pager(
            config = PagingConfig(
                pageSize = pageSize,
                enablePlaceholders = false,
                maxSize = pageSize * 3
            ),
            pagingSourceFactory = { UserPagingSource(api, database) }
        ).flow.cachedIn(ioScope) // 使用作用域缓存Flow
    }
    
    data class SyncResult(
        val successful: List<User>,
        val errors: List<Throwable>
    )
}

8. 测试中的作用域

8.1 测试作用域

class CoroutineTestExample {
    
    @Test
    fun testViewModelCoroutines() = runTest {
        // runTest 提供 TestScope 和 TestDispatcher
        
        val viewModel = UserViewModel(
            userRepository = mockRepository,
            analytics = mockAnalytics
        )
        
        // 1. 测试正常流程
        viewModel.loadUser("123")
        
        // 推进时间
        advanceTimeBy(1000)
        
        // 验证状态
        assertEquals(UiState.Success(expectedUser), viewModel.uiState.value)
        
        // 2. 测试取消
        val job = viewModelScope.launch {
            viewModel.loadUserDetails("123")
        }
        
        advanceTimeBy(500)
        job.cancel() // 模拟用户取消
        
        // 验证取消后的状态
        assertEquals(UiState.Idle, viewModel.uiState.value)
        
        // 3. 测试超时
        viewModel.loadUser("timeout-user")
        
        advanceTimeBy(11000) // 超过10秒超时
        
        assertEquals(UiState.Error("Request timeout"), viewModel.uiState.value)
    }
    
    @Test
    fun testRepositoryConcurrency() = runTest {
        val testScope = this
        
        val repository = UserRepository(
            api = mockApi,
            database = mockDatabase,
            ioScope = testScope // 注入测试作用域
        )
        
        // 测试并发请求
        val results = mutableListOf<User>()
        
        // 启动多个并发请求
        repeat(10) { i ->
            launch {
                val user = repository.getUser("user$i")
                results.add(user)
            }
        }
        
        // 等待所有完成
        advanceUntilIdle()
        
        assertEquals(10, results.size)
    }
}

总结

Kotlin 协程作用域是结构化并发的核心,正确使用作用域可以:

  1. 管理生命周期:自动取消,避免内存泄漏
  2. 组织代码结构:清晰的父子关系,易于理解和维护
  3. 控制并发:通过作用域限制并发数量
  4. 统一错误处理:集中的异常处理机制
  5. 资源管理:自动清理资源

关键原则:

  • 优先使用 lifecycleScopeviewModelScope(Android)
  • 避免使用 GlobalScope,除非是真正的全局任务
  • 使用 coroutineScopesupervisorScope 构建结构化并发
  • 为不同的任务类型创建专门的作用域
  • 始终考虑取消和异常处理