1. Kotlin 协程的核心概念
1.1 协程(Coroutine)
- 定义:协程是一种轻量级的执行上下文,可以在任何时候挂起和恢复,而不需要阻塞线程。
- 特点:
- 比传统线程更轻量,开销更小。
- 支持挂起和恢复,避免了阻塞线程和资源浪费。
- 提供更简洁的并发编程方式。
1.2 挂起函数(Suspend Function)
- 定义:使用
suspend
关键字修饰的函数,可以在协程中挂起执行,挂起期间不会阻塞线程。 - 作用:允许协程在等待任务完成时释放线程资源,待任务完成后恢复执行。
1.3 协程作用域(Coroutine Scope)
- 定义:协程作用域定义了协程的生命周期,确保协程在作用域结束时被取消。
- 常见作用域:
GlobalScope
:全局作用域,生命周期与整个应用程序一致(不推荐用于实际开发)。CoroutineScope
:自定义作用域,可通过CoroutineScope(Dispatchers)
创建。runBlocking
:用于测试场景,会阻塞当前线程直到协程完成。
1.4 协程构建器(Coroutine Builders)
- launch:启动一个协程,不返回结果。
- async:启动一个协程,并返回一个
Deferred
对象,可用于获取结果。 - runBlocking:阻塞当前线程,直到协程完成(仅用于测试)。
2. Kotlin 协程的使用方法
以下是协程使用的基本步骤:
2.1 添加依赖项
在项目的 build.gradle
文件中添加 Kotlin 协程库依赖:
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0'
}
2.2 创建协程作用域
协程必须在协程作用域中运行。例如:
import kotlinx.coroutines.*
fun main() = runBlocking { // 用于测试,实际开发中避免使用
val scope = CoroutineScope(Dispatchers.Default)
scope.launch {
// 在这里执行异步任务
}
}
2.3 启动协程
使用 launch
或 async
启动协程:
- launch:用于不需要返回结果的异步任务。
- async:用于需要返回结果的异步任务。
scope.launch {
delay(1000L) // 模拟耗时操作
println("异步任务完成")
}
val deferred = scope.async {
delay(1000L)
"异步任务结果"
}
println("异步任务返回值: ${deferred.await()}")
2.4 使用挂起函数
挂起函数允许协程在等待任务时挂起,例如 delay
:
suspend fun fetchData(): String {
delay(1000L) // 模拟网络请求
return "Data fetched"
}
3. Kotlin 协程的具体示例
以下通过几个常见场景展示协程的实际用法。
3.1 简单的异步任务
使用 launch
启动一个异步任务:
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello,")
}
输出:
Hello,
World!
3.2 并行任务与结果获取
使用 async
和 await
实现并行任务:
import kotlinx.coroutines.*
fun main() = runBlocking {
val deferred1 = async {
fetchDataFromNetwork1()
}
val deferred2 = async {
fetchDataFromNetwork2()
}
println("Data1: ${deferred1.await()}")
println("Data2: ${deferred2.await()}")
}
suspend fun fetchDataFromNetwork1(): String {
delay(1000L)
return "NetworkData1"
}
suspend fun fetchDataFromNetwork2(): String {
delay(1000L)
return "NetworkData2"
}
3.3 取消协程
使用 cancel
方法取消协程:
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
delay(5000L)
println("Task completed")
}
delay(1000L)
job.cancel()
println("Coroutine cancelled")
}
4.实际应用示例
网络请求与数据库操作
// 假设有以下挂起函数
suspend fun fetchUserData(): UserData = withContext(Dispatchers.IO) {
// 模拟网络请求
delay(1000)
UserData("John", 30)
}
suspend fun saveToDatabase(user: UserData) = withContext(Dispatchers.IO) {
// 模拟数据库操作
delay(500)
println("User saved: $user")
}
data class UserData(val name: String, val age: Int)
fun main() = runBlocking {
// 顺序执行
val user = fetchUserData()
saveToDatabase(user)
// 并发执行多个网络请求
val users = listOf("user1", "user2", "user3")
val deferredList = users.map { userId ->
async { fetchUserDataForId(userId) }
}
val results = deferredList.awaitAll()
println("All users fetched: $results")
}
suspend fun fetchUserDataForId(userId: String): UserData {
delay(1000)
return UserData("$userId-Name", 20 + userId.last().digitToInt())
}
5. 总结
- 核心概念:协程、挂起函数、协程作用域和协程构建器是协程编程的基础。
- 使用方法:通过添加依赖、创建作用域、启动协程和使用挂起函数,可以轻松实现异步编程。
- 适用场景:协程适用于网络请求、文件 IO、CPU 密集型任务等需要并发处理的场景。