一、协程概述
协程是 Kotlin 中处理异步操作的核心特性,它轻量、高效,允许以同步方式编写异步代码,避免回调地狱,提升代码可读性和可维护性。
特性 |
协程 |
线程 |
内存占用 |
约 1KB / 协程 |
约 1MB / 线程 |
启动速度 |
纳秒级 |
毫秒级 |
并发能力 |
数千个 |
数百个 |
资源消耗 |
极低 |
高 |
(数据来源:Kotlin 官方文档及测试数据)
// 在主线程启动协程 viewModelScope.launch(Dispatchers.Main) { // 执行UI操作 } // 在后台线程执行耗时任务 viewModelScope.launch(Dispatchers.IO) { val data = repository.fetchData() withContext(Dispatchers.Main) { textView.text = data } } |
val deferred = viewModelScope.async(Dispatchers.IO) { repository.fetchUserData() } // 等待结果并在主线程处理 viewModelScope.launch(Dispatchers.Main) { val user = deferred.await() updateUI(user) } |
在 Kotlin 协程中,async函数的核心特性是异步启动但支持同步获取结果,它的执行机制可以拆解为两部分:
async的启动机制与launch一致,都是通过协程调度器实现非阻塞启动:
Deferred.await()方法具有以下特点:
特性 |
传统同步方法 |
async+await 组合 |
纯异步回调 |
调用线程阻塞 |
是(线程级阻塞) |
否(协程级挂起) |
否 |
结果获取方式 |
立即返回 |
显式 await () 获取 |
回调函数 |
并行能力 |
无 |
强(支持 async 并行) |
强 |
代码可读性 |
高 |
高(接近同步写法) |
低(可能出现回调地狱) |
class MyRepository(private val scope: CoroutineScope) { fun fetchData() { scope.launch(Dispatchers.IO) { // 执行数据库操作 } } } |
// 挂起函数必须标记suspend suspend fun fetchUserData(userId: String): User { delay(1000L) // 模拟网络请求 return User(userId, "John Doe") } // 在协程中调用挂起函数 viewModelScope.launch { val user = fetchUserData("123") updateUI(user) } |
viewModelScope.launch(Dispatchers.IO) { // 执行耗时操作 val data = database.loadData() //从IO线程切换到Main线程 withContext(Dispatchers.Main) { // 更新UI } } |
//所有子协程完成后才会结束 //任一子协程异常会取消整个作用域 suspend fun fetchMultipleData(): CombinedData { return coroutineScope { val userDeferred = async { fetchUser() } val profileDeferred = async { fetchProfile() } CombinedData(userDeferred.await(), profileDeferred.await()) } } |
coroutineScope 是 Kotlin 协程中实现结构化并发的核心 API,其设计目标是解决传统异步编程中任务生命周期管理混乱的问题。它的核心特性包括:
自动资源释放:确保协程执行完毕后释放相关资源
coroutineScope 通过以下机制实现结构化并发:
// 作用域树结构示例 suspend fun parentScope() = coroutineScope { println("父作用域开始")
// 第一个子作用域 coroutineScope { println("子作用域1开始") launch { delay(1000) println("子作用域1任务完成") } println("子作用域1等待任务完成") }
// 第二个子作用域 coroutineScope { println("子作用域2开始") launch { delay(500) println("子作用域2任务完成") } println("子作用域2等待任务完成") }
println("父作用域所有子任务完成") } |
coroutineScope 的异常处理遵循以下规则:
suspend fun exceptionHandling() = coroutineScope { val job1 = launch { delay(1000) println("任务1完成") }
val job2 = launch { delay(500) throw IOException("网络错误") // 抛出异常 }
// 以下代码不会执行,因为job2抛出异常 job1.join() job2.join() println("所有任务完成") } // 调用方式 try { exceptionHandling() } catch (e: IOException) { println("捕获异常: ${e.message}") // 输出:捕获异常: 网络错误 } |
特性 |
coroutineScope |
GlobalScope |
viewModelScope |
结构化并发 |
是(强制等待子协程) |
否 |
是(与生命周期绑定) |
异常处理 |
严格(异常传播) |
无 |
自动取消(随组件销毁) |
生命周期管理 |
自动(完成即结束) |
手动 |
自动(随 ViewModel 销毁) |
适用场景 |
并行任务聚合 |
后台长时间运行任务 |
ViewModel 内异步操作 |
supervisorScope是 Kotlin 协程中用于实现非结构化并发的关键构建器,它与coroutineScope共同构成了结构化并发的两大支柱。其核心特性在于:
// supervisorScope基本结构 suspend fun supervisorScopeDemo() { supervisorScope { // 启动多个子协程 val job1 = launch { /* 任务1 */ } val job2 = launch { /* 任务2 */ } val job3 = launch { /* 任务3 */ }
// 等待所有子协程完成 job1.join() job2.join() job3.join() } } |
特性 |
coroutineScope |
supervisorScope |
异常传播 |
子协程异常会取消整个作用域及所有兄弟协程 |
子协程异常仅影响自身,不影响其他协程 |
异常处理责任 |
父协程负责统一处理所有子协程异常 |
每个子协程需自行处理自身异常 |
任务完整性 |
所有子任务必须全部完成 |
部分子任务失败不影响其他任务执行 |
适用场景 |
强一致性要求的场景(如数据库事务) |
独立任务并行执行的场景(如日志收集) |
// coroutineScope异常传播示例 suspend fun coroutineScopeException() { try { coroutineScope { launch { delay(100) throw Exception("子协程1异常") } launch { delay(200) println("子协程2是否执行?") // 不会执行 } } } catch (e: Exception) { println("捕获到异常: ${e.message}") } } // supervisorScope异常隔离示例 suspend fun supervisorScopeException() { try { supervisorScope { launch { delay(100) throw Exception("子协程1异常") } launch { delay(200) println("子协程2正常执行") // 会执行 } } } catch (e: Exception) { println("捕获到异常: ${e.message}") // 不会捕获到异常 } } |
graph TD A[supervisorScope] --> B[子协程1] A --> C[子协程2] B -->|抛出异常| B1[异常仅在子协程1内传播] C -->|正常执行| C1[子协程2不受影响] |
suspend fun selfHandleException() { supervisorScope { // 子协程1:自行处理异常 launch { try { // 可能抛出异常的操作 throw IOException("网络异常") } catch (e: IOException) { logError("网络请求失败: ${e.message}") } }
// 子协程2:未处理异常会导致自身取消,但不影响其他协程 launch { // 未处理的异常 throw IllegalStateException("状态异常") }
// 子协程3:正常执行 launch { delay(500) println("子协程3完成") } } } |
// 定义Flow fun getUpdates(): Flow while (true) { emit(fetchUpdate()) // 发射数据 delay(1000L) } } // 收集Flow lifecycleScope.launch { getUpdates() .flowOn(Dispatchers.IO) .collect { update -> // 更新UI } } |
val channel = Channel // 生产者 launch { for (i in 1..5) { channel.send(i * i) } channel.close() } // 消费者 launch { for (value in channel) { println(value) } } |
// 容量为2,溢出时丢弃最旧数据 val channel = Channel |
class MyViewModel : ViewModel() { private val _uiState = MutableStateFlow val uiState: StateFlow fun fetchData() { 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 ?: "未知错误") } } } } |
class MyViewModel : ViewModel() { val userData: LiveData .flowOn(Dispatchers.IO) .catch { e -> Log.e("MyViewModel", "获取数据失败", e) } .asLiveData(viewModelScope.coroutineContext) } |
suspend fun fetchMultipleData(): CombinedData { return coroutineScope { val userDeferred = async(Dispatchers.IO) { userService.getUser() } val profileDeferred = async(Dispatchers.IO) { profileService.getProfile() } CombinedData(userDeferred.await(), profileDeferred.await()) } } |
import kotlinx.coroutines.test.* @Test fun testFetchData() = runTest { val testDispatcher = StandardTestDispatcher() Dispatchers.setMain(testDispatcher) val viewModel = MyViewModel(repository = mockRepository) viewModel.fetchData() // 推进时间让协程执行 testDispatcher.advanceUntilIdle() assertEquals(UiState.Success(expectedData), viewModel.uiState.value) } |
@Test fun testNetworkRequest() = runTest { val mockRepository = MockRepository() val viewModel = MyViewModel(repository = mockRepository)
// 模拟网络请求延迟 mockRepository.mockDelay(1000L)
viewModel.fetchData() advanceTimeBy(1000L) // 推进时间
assertTrue(viewModel.uiState.value is UiState.Success) } |
Kotlin 协程是 Android 异步编程的革命性工具,通过轻量级、结构化并发和响应式编程,极大提升了代码的可读性和可维护性。掌握协程的基础、进阶技巧和最佳实践,能够显著提高开发效率,减少内存泄漏和线程管理问题。建议结合官方文档和实际项目不断实践,逐步深入理解协程的核心原理和应用场景。