Kotlin协程完全指南:从基础到架构设计实战
Kotlin协程作为现代异步编程的利器,已经彻底改变了Android和服务器端开发的方式。本文将带您从协程基础概念开始,逐步深入高级用法,最终掌握在大型项目中的架构设计实践,每个阶段都配有实用的代码示例。
一、协程基础与核心概念
1.1 协程的本质与优势
协程是轻量级线程,具有以下核心特性:
- 轻量:单个线程可运行数千协程
- 结构化并发:内置取消和错误传播机制
- 挂起函数:以同步方式写异步代码
kotlin
// 第一个协程示例
fun main() = runBlocking { // 创建根协程
launch { // 在后台启动新协程
delay(1000L) // 非阻塞延迟1秒
println("World!")
}
println("Hello") // 主协程继续执行
}
// 输出: Hello -> (1秒后) World!
1.2 关键构建块
协程API的核心组件:
kotlin
// 1. CoroutineScope - 协程作用域
val scope = CoroutineScope(Dispatchers.Default + Job())
// 2. 构建器
scope.launch { /* 不返回结果 */ }
val deferred = scope.async { /* 返回Deferred<T> */ }
// 3. 调度器
Dispatchers.Main // Android主线程
Dispatchers.IO // 磁盘/网络IO
Dispatchers.Default // CPU密集型工作
// 4. 挂起函数
suspend fun fetchUser(): User {
delay(1000) // 模拟网络请求
return User("John")
}
二、结构化并发实践
2.1 作用域与生命周期管理
kotlin
class MyViewModel : ViewModel() {
private val viewModelScope = CoroutineScope(
SupervisorJob() + Dispatchers.Main.immediate
)
fun fetchData() {
viewModelScope.launch {
try {
val data = repository.loadData()
updateUI(data)
} catch (e: Exception) {
showError(e)
}
}
}
override fun onCleared() {
viewModelScope.cancel() // 自动取消所有子协程
}
}
2.2 并发模式与异常处理
kotlin
suspend fun fetchUserAndPosts(): Pair<User, List<Post>> = coroutineScope {
val userDeferred = async { api.getUser() }
val postsDeferred = async { api.getPosts() }
try {
// 并发执行两个请求
userDeferred.await() to postsDeferred.await()
} catch (e: Exception) {
// 如果一个失败,另一个会自动取消
throw FetchException("Failed to load data", e)
}
}
三、高级协程模式
3.1 Channel与Flow
kotlin
// 热数据流 - Channel
fun CoroutineScope.produceNumbers() = produce {
var x = 1
while (true) {
send(x++)
delay(100)
}
}
// 冷数据流 - Flow
fun fibonacci(): Flow<BigInteger> = flow {
var a = BigInteger.ZERO
var b = BigInteger.ONE
while (true) {
emit(a)
delay(100)
val temp = a
a = b
b += temp
}
}
// Flow操作符
fun main() = runBlocking {
fibonacci()
.take(10)
.filter { it.isProbablePrime(100) }
.collect { println(it) }
}
3.2 复杂并发控制
kotlin
// 使用Mutex保护共享状态
class SharedCounter {
private var counter = 0
private val mutex = Mutex()
suspend fun increment() {
mutex.withLock {
counter++
}
}
}
// 使用Semaphore限制并发
val semaphore = Semaphore(3) // 最多3个并发
suspend fun limitedResource() {
semaphore.withPermit {
// 访问受限资源
delay(1000)
}
}
四、协程与架构设计
4.1 分层架构中的协程
kotlin
// 数据层
class UserRepository(
private val localDataSource: UserLocalDataSource,
private val remoteDataSource: UserRemoteDataSource
) {
suspend fun getUser(userId: String): User {
return withContext(Dispatchers.IO) {
val cached = localDataSource.getUser(userId)
if (cached == null) {
val remote = remoteDataSource.getUser(userId)
localDataSource.saveUser(remote)
remote
} else {
cached
}
}
}
}
// 领域层
class GetUserUseCase(
private val userRepository: UserRepository
) {
suspend operator fun invoke(userId: String): User {
return userRepository.getUser(userId)
}
}
// 表现层
class UserViewModel(
private val getUserUseCase: GetUserUseCase
) : ViewModel() {
private val _user = MutableStateFlow<User?>(null)
val user: StateFlow<User?> = _user
fun loadUser(userId: String) {
viewModelScope.launch {
_user.value = getUserUseCase(userId)
}
}
}
4.2 响应式UI与协程
kotlin
@Composable
fun UserProfile(userId: String) {
val viewModel: UserViewModel = viewModel()
val user by viewModel.user.collectAsState()
LaunchedEffect(userId) {
viewModel.loadUser(userId)
}
when {
user == null -> CircularProgressIndicator()
else -> ProfileContent(user!!)
}
}
五、性能优化与调试
5.1 协程调试技巧
kotlin
// 1. 添加协程名称
CoroutineScope(Dispatchers.IO + CoroutineName("NetworkScope"))
// 2. 使用DebugProbes检测泄漏
DebugProbes.install()
DebugProbes.dumpCoroutines() // 打印所有活跃协程
// 3. 自定义异常处理器
val handler = CoroutineExceptionHandler { _, throwable ->
Log.e("CoroutineError", "Uncaught exception", throwable)
}
5.2 性能优化实践
kotlin
// 1. 避免过度调度
suspend fun processImage(image: Image) = withContext(Dispatchers.Default) {
// CPU密集型操作
}
// 2. 批量处理
suspend fun batchProcess(items: List<Item>) = coroutineScope {
items.chunked(50) { chunk -> // 分块处理
launch {
processChunk(chunk)
}
}.joinAll()
}
// 3. 使用缓存Flow
val userFlow = flow {
emit(repository.getUser())
}.shareIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
replay = 1
)
六、跨平台协程应用
6.1 KMM中的协程使用
kotlin
// 共享模块
class Greeting {
private val scope = CoroutineScope(Dispatchers.Default)
fun greet(callback: (String) -> Unit) {
scope.launch {
val greeting = withContext(Dispatchers.Default) {
"Hello from ${Platform().platform}"
}
callback(greeting)
}
}
}
// Android实现
class AndroidPlatform : Platform {
override val platform: String = "Android"
}
// iOS实现
class IOSPlatform : Platform {
override val platform: String = "iOS"
}
6.2 服务端协程应用
kotlin
// Ktor路由示例
fun Application.configureRouting() {
routing {
get("/user/{id}") {
val id = call.parameters["id"] ?: throw BadRequestException()
val user = userService.getUser(id) // 挂起函数
call.respond(user)
}
post("/upload") {
val multipart = call.receiveMultipart()
multipart.forEachPart { part ->
when (part) {
is PartData.FileItem -> {
fileService.save(part.streamProvider())
}
}
part.dispose()
}
call.respond(HttpStatusCode.OK)
}
}
}
七、测试协程代码
7.1 单元测试
kotlin
class UserRepositoryTest {
@get:Rule
val coroutineRule = MainCoroutineRule() // 提供TestDispatcher
@Test
fun `load user from remote when local is empty`() = runTest {
// 准备
val local = FakeLocalDataSource(users = emptyMap())
val remote = FakeRemoteDataSource(users = mapOf("1" to User("1", "John")))
val repo = UserRepository(local, remote)
// 执行
val user = repo.getUser("1")
// 验证
assertEquals("John", user.name)
}
}
// 测试规则
class MainCoroutineRule : TestWatcher() {
val testDispatcher = StandardTestDispatcher()
override fun starting(description: Description) {
Dispatchers.setMain(testDispatcher)
}
override fun finished(description: Description) {
Dispatchers.resetMain()
}
}
7.2 集成测试
kotlin
class UserFlowTest {
@Test
fun `user flow emits loading then data`() = runTest {
val viewModel = UserViewModel(
userRepository = MockUserRepository(delay = 1000)
)
val results = mutableListOf<UiState<User>>()
val job = viewModel.userState.collect { results.add(it) }
viewModel.loadUser("1")
advanceTimeBy(500) // 模拟时间流逝
assertEquals(1, results.size)
assertTrue(results[0] is UiState.Loading)
advanceTimeBy(1000)
assertEquals(2, results.size)
assertTrue(results[1] is UiState.Success)
job.cancel()
}
}
结语
Kotlin协程通过结构化并发和挂起函数的概念,彻底简化了异步编程的复杂性。从简单的后台任务到复杂的并发操作,再到跨平台的架构设计,协程都展现出了强大的表现力。
在实际项目中应用协程时,建议:
- 遵循分层原则:明确各层协程的使用边界
- 合理选择调度器:根据任务类型选择适当的Dispatcher
- 管理生命周期:使用自定义CoroutineScope避免泄漏
- 监控协程状态:利用DebugProbes和自定义异常处理器
- 编写可测试代码:通过依赖注入和接口隔离提高可测试性
随着Kotlin语言的持续发展,协程也在不断进化。建议关注Kotlin官方博客和协程更新日志,及时获取关于Flow新操作符、多平台改进等最新特性。掌握协程将极大提升您的异步编程能力和代码质量,是现代Kotlin开发者不可或缺的核心技能。