🚀 为什么选择协程?
📖 什么是协程?
**协程(Coroutines)**是 Kotlin 的核心功能,它让异步编程变得简单而直观。从复杂的回调地狱中解脱出来,像编写同步代码一样编写异步代码!
💡 传统异步的问题
回调地狱
// ❌ 回调方式(难以阅读和维护)
fun fetchUser(userId: String, callback: (User?) -> Unit) {
fetchFromNetwork(userId) { result ->
if (result != null) {
fetchProfile(result.id) { profile ->
if (profile != null) {
fetchPosts(profile.id) { posts ->
callback(User(result, profile, posts))
}
} else {
callback(null)
}
}
} else {
callback(null)
}
}
}
线程的开销
// ❌ 线程很昂贵
fun downloadFiles() {
repeat(1000) {
Thread {
// 每个线程占用大量内存
downloadFile("file_$it.txt")
}.start()
}
// 可能内存不足!
}
✨ 协程的优势
1. 简洁的代码
// ✅ 协程(易读且直观)
suspend fun fetchUser(userId: String): User? {
val result = fetchFromNetwork(userId) ?: return null
val profile = fetchProfile(result.id) ?: return null
val posts = fetchPosts(profile.id)
return User(result, profile, posts)
}
2. 轻量级并发
// ✅ 数千个协程也没问题
suspend fun downloadFiles() {
coroutineScope {
repeat(10000) {
launch {
// 轻量级协程!
downloadFile("file_$it.txt")
}
}
}
}
3. 结构化并发
// ✅ 自动管理生命周期
suspend fun processData() {
coroutineScope {
val job1 = launch { task1() }
val job2 = launch { task2() }
// 所有任务结束后自动完成
}
}
🎯 实际使用示例
网络请求
// 无需回调,简洁明了!
suspend fun loadUserData(userId: String): UserData {
val user = apiService.getUser(userId)
val posts = apiService.getPosts(userId)
val friends = apiService.getFriends(userId)
return UserData(user, posts, friends)
}
UI 更新
// 不阻塞主线程
suspend fun updateUI() {
val data = withContext(Dispatchers.IO) {
// 在后台加载数据
loadDataFromDatabase()
}
// 在主线程更新 UI
updateViews(data)
}
并行处理
// 同时执行多个任务!
suspend fun fetchAllData(): CombinedData = coroutineScope {
val users = async { fetchUsers() }
val products = async { fetchProducts() }
val orders = async { fetchOrders() }
CombinedData(
users.await(),
products.await(),
orders.await()
)
}
🔥 协程 vs 线程
| 特性 | 协程 | 线程 |
|---|---|---|
| 重量 | 非常轻量 | 重量级 |
| 创建成本 | 几乎为零 | 昂贵 |
| 数量 | 可达数万个 | 数千个为限 |
| 切换成本 | 低 | 高 |
| 取消 | 简单 | 困难 |
性能比较
// 协程:运行 100,000 个
suspend fun testCoroutines() {
val time = measureTimeMillis {
coroutineScope {
repeat(100_000) {
launch {
delay(1000)
}
}
}
}
println("协程: ${time}ms") // ~1000ms
}
// 线程:即使只有 10,000 个...
fun testThreads() {
val time = measureTimeMillis {
repeat(10_000) {
Thread {
Thread.sleep(1000)
}.start()
}
}
println("线程: ${time}ms") // 可能内存不足!
}
🎨 协程的核心概念
Suspend(挂起)
// suspend 函数只能在协程内调用
suspend fun doWork() {
delay(1000) // 不阻塞线程而是等待
println("任务完成")
}
结构化并发
// 通过父子关系管理生命周 期
fun main() = runBlocking {
launch { // 父级
launch { // 子级 1
delay(1000)
println("子级 1 完成")
}
launch { // 子级 2
delay(2000)
println("子级 2 完成")
}
println("父级等待子级结束")
}
}
🤔 常见问题
Q1. 协程是新的线程吗?
A: 不是!协程是在线程上运行的轻量级工作单元。
// 在一个线程上可以运行多个协程
fun main() = runBlocking {
repeat(3) { i ->
launch {
println("协程 $i: ${Thread.currentThread().name}")
}
}
}
// 它们都可以在同一个线程上运行!
Q2. 需要把所有函数都改成 suspend 吗?
A: 不需要!只有需要异步操作的函数才需要改成 suspend。
// ❌ 不需要
suspend fun add(a: Int, b: Int) = a + b
// ✅ 需要
suspend fun fetchData() = withContext(Dispatchers.IO) {
loadFromNetwork()
}
Q3. 什么时候应该使用协程?
A: 在这些情况下使用!
// ✅ 网络请求
suspend fun loadData() = api.fetchData()
// ✅ 数据库操作
suspend fun saveUser(user: User) = db.insert(user)
// ✅ 文件 I/O
suspend fun readFile(path: String) = withContext(Dispatchers.IO) {
File(path).readText()
}
// ✅ 长时间计算任务
suspend fun heavyComputation() = withContext(Dispatchers.Default) {
// 复杂计算
}
🎬 总结
用协程让异步编程变简单!
核心要点:
✅ 摆脱回调地狱
✅ 轻量级并发
✅ 直观的代码
✅ 结构化的生命周期
✅ 比线程更高效
下一步: 在协程基础中实际使用协程!