协程不绑定任何线程——它是线程的过客,Dispatcher 是派工系统
| 概念 | 角色 | 说明 |
|---|---|---|
| 协程 | 工单 | 你要干的活——一段代码 + 一个状态机对象 |
| 线程 | 工人 | 干活的人——OS 内核管理的执行单元 |
| Dispatcher | 派工系统 | 把工单分给哪个工人——决定协程跑在哪个线程上 |
launch { // 这是一个协程(工单)
println("干活") // 这个活
} // 由调度器决定交给哪个线程执行
协程本身不绑定任何线程。它只是一段代码 + 一个状态机对象。谁跑它,由 Dispatcher 说了算。
launch(Dispatchers.Main) {
// 在这里更新 UI
textView.text = "hello"
}
launch(Dispatchers.IO) {
val data = httpClient.get("https://...") // 在 IO 线程上跑
withContext(Dispatchers.Main) {
textView.text = data // 切回主线程更新 UI
}
}
launch(Dispatchers.Default) {
// 排序大数组、解析 JSON、加密...
val sorted = bigList.sorted()
}
launch(Dispatchers.Unconfined) {
println("在调用者的线程上开始")
delay(100)
println("在恢复线程上继续(可能是另一个)")
}
| Dispatcher | 线程池大小 | 适用场景 | 典型操作 |
|---|---|---|---|
| Main | 1 个(UI 线程) | UI 更新 | setText()、更新 LiveData/StateFlow |
| IO | 64 个(按需扩缩) | 网络、文件 | HTTP 请求、文件读写、数据库 |
| Default | CPU 核数个 | 计算密集 | 排序、加解密、JSON 解析 |
| Unconfined | 无固定 | 特殊场景 | 测试、不关心线程的轻量操作 |
launch(Dispatchers.IO) {
println("挂起前:${Thread.currentThread().name}") // IO-thread-1
delay(1000) // 挂起!
// 挂起期间,IO-thread-1 去跑别的协程了
println("恢复后:${Thread.currentThread().name}") // IO-thread-3(可能不同!)
}
协程从不占有线程。它是线程的过客。创建→Dispatcher队列→某线程执行→挂起→离开→恢复→可能换另一个线程→再挂起→再换……
launch(Dispatchers.Main) { // 主线程
val result = withContext(Dispatchers.IO) { // 切到 IO 线程
heavyNetworkCall() // 在 IO 线程跑
} // ← 自动切回主线程
textView.text = result // 回到主线程更新 UI
}
withContext 做的事:挂起当前协程 → 在新线程跑代码 → 跑完把结果传回 → 在原线程恢复。不需要回调、不需要 Handler.post、不需要 runOnUiThread。
协程不绑定线程,它只是一段活在堆上的状态机代码。Dispatcher 决定它跑到哪个线程上:Main→UI、IO→网络文件、Default→CPU计算。挂起前在 Thread-1,恢复后可能在 Thread-3——完全透明。withContext 临时换线程,跑完自动回来,零回调。