协程是Kotlin的异步编程解决方案,类似于Swift的 async/await
suspend 函数
suspend 是 Kotlin 协程的核心关键字,标记一个函数可以被挂起。挂起的意思是:函数执行到某个点时可以暂停,释放当前线程去做别的事,等结果回来再继续,整个过程不阻塞线程。
// Kotlin
suspend fun fetchUser(): User { ... }
// Swift
func fetchUser() async -> User { ... }
`async` = `suspend`,概念完全一样
suspend 函数的规则
// Kotlin
suspend fun loadData() {
val user = fetchUser() // ✅ suspend 函数里调用 suspend 函数
}
fun normalFunc() {
val user = fetchUser() // ❌ 编译报错,必须在协程或 suspend 函数里
}
// Swift — async 函数只能在 async 上下文调用
func loadData() async {
let user = await fetchUser() // ✅
}
func normalFunc() {
let user = await fetchUser() // ❌ 编译报错
}
启动协程
suspend 函数不能凭空调用,需要一个入口来启动协程,主要有三种方式。
launch— 不关心返回值
// Kotlin
viewModelScope.launch {
loadUser()
}
// Swift
Task {
await loadUser()
}
`launch` 返回一个 `Job` 对象,可以用来取消,但**拿不到协程的执行结果**:
val job = viewModelScope.launch {
loadUser()
}
job.cancel() // 可以取消
// job 拿不到 loadUser() 的返回值
async— 关心返回值
// Kotlin
val deferred = viewModelScope.async {
fetchUser()
}
val result = deferred.await() // 等待拿结果
对应 Swift 的 `async let`:
// Swift
async let user = fetchUser()
let result = await user
`async` 返回 `Deferred<T>`,调用 `.await()` 才能拿到值。
最常见的用法是并行请求
// Kotlin
fun loadAll() {
viewModelScope.launch {
val user = async { fetchUser() }
val posts = async { fetchPosts() }
val u = user.await()
val p = posts.await()
}
}
// Swift
func loadAll() async {
async let user = fetchUser()
async let posts = fetchPosts()
let u = try? await user
let p = try? await posts
}
runBlocking— 测试专用
这个 Swift 没有对应的,它的作用是把协程变成阻塞调用,主要用于单元测试:
// 普通函数里没法调用 suspend 函数
// runBlocking 强行创建一个阻塞的协程环境
fun main() {
runBlocking {
val user = fetchUser() // 现在可以调用了
println(user)
}
}
⚠️ 生产代码里不要用,会阻塞线程,只在测试和 main 函数里用。
launch vs async 怎么选
需要返回值吗?
├── 不需要 → launch
└── 需要 → async + await
Scope 作用域
Scope 定义了协程的生命周期,协程必须在某个 Scope 里运行,Scope 取消时它里面所有协程都会取消。
viewModelScope
class UserViewModel : ViewModel() {
fun loadUser() {
viewModelScope.launch {
val user = fetchUser()
_uiState.value = user
}
}
}
// ViewModel 被清除时,所有协程自动取消
// Swift UIKit — VC 都 deinit 了,Task 还在跑
class UserViewController: UIViewController {
func loadUser() {
Task {
let user = await fetchUser()
self.updateUI(user) // VC 都没了还在回调,可能 crash
}
}
// 要自己存 task 然后手动 cancel
var task: Task<Void, Never>?
override func viewDidDisappear(_ animated: Bool) {
task?.cancel() // 还得记得取消
}
}
lifecycleScope
用在 Activity / Fragment 里,和 Activity 生命周期绑定:
class UserActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 基础用法,Activity 销毁自动取消
lifecycleScope.launch {
loadData()
}
// 进阶用法:感知前后台
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
// App 进后台自动暂停,回前台自动恢复
viewModel.uiState.collect { state ->
updateUI(state)
}
}
}
}
}
// Swift UIKit — 你可能这样写过
class UserViewController: UIViewController {
var task: Task<Void, Never>?
override func viewDidAppear(_ animated: Bool) {
task = Task {
for await state in viewModel.uiStateStream {
updateUI(state)
}
}
}
override func viewDidDisappear(_ animated: Bool) {
task?.cancel() // 手动取消
}
}
`lifecycleScope + repeatOnLifecycle` 把这套模板代码帮你封装好了。
CoroutineScope手动创建
用在普通类里,比如 Repository、Manager,这些类没有框架提供的 Scope:
class UserRepository {
// 手动创建 scope
private val scope = CoroutineScope(
Dispatchers.IO + SupervisorJob()
)
fun startSync() {
scope.launch {
syncData()
}
}
// ⚠️ 必须手动取消,否则内存泄漏
fun destroy() {
scope.cancel()
}
}
// Swift
class UserRepository {
var tasks: [Task<Void, Never>] = []
func startSync() {
let task = Task {
await syncData()
}
tasks.append(task)
}
func destroy() {
tasks.forEach { $0.cancel() }
}
}
两者都需要手动管理,Kotlin 只是用 `scope.cancel()` 一次性取消所有。
GlobalScope — 几乎不用-----未完待续
coroutineScope {} 和 supervisorScope {}
`coroutineScope` — 一个失败全取消
//Kotlin 对应 Swift 的 async let,行为完全一样
suspend fun loadAll(): Pair<User, List<Post>> = coroutineScope {
val user = async { fetchUser() }
val posts = async { fetchPosts() }
// fetchUser 抛异常 → fetchPosts 也取消
Pair(user.await(), posts.await())
}
// Swift async let 也是同样行为
func loadAll() async throws -> (User, [Post]) {
async let user = fetchUser()
async let posts = fetchPosts()
// fetchUser 抛异常 → fetchPosts 也取消
return try await (user, posts)
}
### `supervisorScope` — 失败互不影响
suspend fun loadAll() = supervisorScope { val user = async { fetchUser() } val posts = async { fetchPosts() } // fetchUser 抛异常 → fetchPosts 继续跑,互不影响
val u = runCatching { user.await() }.getOrNull()
val p = runCatching { posts.await() }.getOrNull()
}
Swift 没有直接对应,你之前用 `try?` 单独处理每个 `await` 来达到类似效果。
Scope 选择总结
你在哪写代码?
│
├── ViewModel 里 → viewModelScope
├── Activity/Fragment 里 → lifecycleScope
├── 普通类里 → CoroutineScope(...) 手动管理
│
└── suspend 函数内部需要并发?
├── 一个失败全取消 → coroutineScope {}
└── 失败互不影响 → supervisorScope {}
Dispatcher 调度器
Dispatcher 决定协程在哪个线程上运行。对应 Swift 里你切线程的方式。
Dispatchers.Main — 主线程
// Kotlin — 主线程,用来更新 UI
viewModelScope.launch(Dispatchers.Main) {
textView.text = "Hello"
}
// 其实 viewModelScope 默认就是 Main
// 所以大部分时候不用显式写
viewModelScope.launch {
textView.text = "Hello" // 已经在主线程了
}
// Swift — 切回主线程
await MainActor.run {
self.tableView.reloadData()
}
Dispatchers.IO — IO 线程
// Kotlin — 网络请求、数据库、文件读写都用这个
viewModelScope.launch {
val data = withContext(Dispatchers.IO) {
api.fetchUser() // 切到 IO 线程
}
textView.text = data.name // 自动切回 Main 线程
}
背后有一个线程池,最多 64 个线程,专门处理等待 IO 的任务。
// Swift — 后台线程做网络/文件
Task.detached(priority: .background) {
let data = await fetchFromNetwork()
}
`withContext` — 切换线程
这是最常用的切线程方式,对应 Swift 的 `await MainActor.run {}`:
Dispatchers.Default — CPU 密集线程
viewModelScope.launch {
val result = withContext(Dispatchers.Default) {
// 大量数据排序、图片处理、JSON 解析
processLargeDataSet(data)
}
updateUI(result)
}
线程数和 CPU 核心数一样,专门跑计算密集的任务。
Swift 没有直接对应,用于**计算量大**的任务:
Dispatchers.Main → UI 操作、更新状态
Dispatchers.IO → 网络请求、数据库、文件
Dispatchers.Default → 排序、计算、图片处理
错误处理
基础 try/catch 和 Swift 的 do/try/catch 几乎一样:
// Kotlin — 结构完全一样
fun loadUser() {
viewModelScope.launch {
try {
val user = fetchUser()
updateUI(user)
} catch (e: Exception) {
showError(e)
}
}
}
// Swift
func loadUser() async {
do {
let user = try await fetchUser()
updateUI(user)
} catch {
showError(error)
}
}
runCatching — 对应 Swift 的 try?
// Kotlin — 失败返回 null
val user = runCatching { fetchUser() }.getOrNull()
// 还可以拿到错误信息
val result = runCatching { fetchUser() }
result.onSuccess { user -> updateUI(user) }
result.onFailure { e -> showError(e) }
// Swift — 失败返回 nil
let user = try? await fetchUser()
CoroutineExceptionHandler — 全局兜底
Swift 没有对应的,这是协程特有的全局错误处理机制:
class UserViewModel : ViewModel() {
private val handler = CoroutineExceptionHandler { _, e ->
_uiState.value = UiState.Error(e.message)
}
fun loadUser() {
viewModelScope.launch(handler) {
val user = fetchUser()
_uiState.value = UiState.Success(user)
}
}
}
launch vs async 的错误处理行为不一样
这是 Kotlin 特有的坑,Swift 没有这个区别:
// launch — 异常立刻抛出
viewModelScope.launch {
throw Exception("出错了") // 立刻崩,必须在里面 catch
}
// async — 异常在 await() 时才抛出
viewModelScope.launch {
val deferred = async {
throw Exception("出错了") // 这里不崩
}
deferred.await() // 异常在这里才抛出
}
所以用 `async` 的时候要在 `await()` 外面 catch:
viewModelScope.launch {
try {
val deferred = async { fetchUser() }
val user = deferred.await() // catch 要包这里
} catch (e: Exception) {
showError(e)
}
}
错误处理选哪种
单个请求出错就展示错误 → try/catch
不关心错误只想要 null → runCatching + getOrNull
全局兜底防止 crash → CoroutineExceptionHandler
取消与超时
基础取消
// Kotlin
val job = viewModelScope.launch {
loadUser()
}
job.cancel() // 取消
// Swift
let task = Task {
await loadUser()
}
task.cancel() // 取消
取消是协作式的 这点和 Swift 完全一样,取消不是强制停止,协程需要配合才能真正取消
// Kotlin — 同样需要配合
suspend fun loadUser() {
for (item in items) {
if (!isActive) return // 主动检查是否还活着
process(item)
}
}
Kotlin 内置的 `suspend` 函数(比如 `delay`、`withContext`)都自动支持取消,不用手动检查。只有你自己写的循环逻辑才需要加 `isActive` 检查。
// Swift — 需要检查 isCancelled
func loadUser() async {
for item in items {
try Task.checkCancellation() // 主动检查
await process(item)
}
}
withTimeout — 超时取消
// Kotlin — 一行搞定
suspend fun fetchWithTimeout(): User {
return withTimeout(3000) { // 3秒超时
fetchUser()
}
// 超时抛出 TimeoutCancellationException
}
// 不想让超时抛异常,返回 null
val user = withTimeoutOrNull(3000) {
fetchUser()
} // 超时返回 null,不崩
// Swift — 没有原生支持,要自己实现
func fetchWithTimeout() async throws -> User {
try await withThrowingTaskGroup(of: User.self) { group in
group.addTask { try await fetchUser() }
group.addTask {
try await Task.sleep(nanoseconds: 3_000_000_000)
throw TimeoutError()
}
let result = try await group.next()!
group.cancelAll()
return result
}
}
取消后的清理工作
// Kotlin — 用 try/finally
suspend fun loadUser() {
try {
showLoading()
val user = fetchUser()
updateUI(user)
} finally {
hideLoading() // 无论成功、失败、取消都执行
}
}
`finally` 在协程被取消时也会执行,和 Swift 的 `defer` 一样可靠。
// Swift — 用 defer 清理
func loadUser() async {
defer {
hideLoading() // 无论成功还是取消都执行
}
showLoading()
let user = try await fetchUser()
}