协程关键字

常见关键字/函数是这些:

launch
async
withContext
Dispatchers.Main
Dispatchers.IO
Dispatchers.Default
Dispatchers.Unconfined

1. 决定协程运行线程的关键字:Dispatchers.xxx

常见调度器:

调度器 运行线程 用途
Dispatchers.Main 主线程/UI 线程 更新 UI
Dispatchers.IO IO 线程池 网络、数据库、文件读写
Dispatchers.Default CPU 计算线程池 排序、加密、图片处理、大量计算
Dispatchers.Unconfined 不固定线程 特殊场景,少用

2. 主线程运行的协程

在 Android 里,下面这些通常默认就是主线程。

1. viewModelScope.launch {}

viewModelScope.launch {
    // 默认在主线程 Main
    textView.text = "hello" // 可以更新 UI,当然 ViewModel 里一般不直接操作 View
}

viewModelScope 默认使用:

Dispatchers.Main.immediate

所以它默认是在主线程。


2. lifecycleScope.launch {}

lifecycleScope.launch {
    // 默认在主线程 Main
    binding.textView.text = "加载中"
}

lifecycleScope 默认也是主线程。


3. 显式指定 Dispatchers.Main

CoroutineScope(Dispatchers.Main).launch {
    // 主线程
}

或者:

launch(Dispatchers.Main) {
    // 主线程
}

3. 后台线程运行的协程

如果你要做耗时任务,一般要指定调度器。

1. IO 任务:Dispatchers.IO

适合:

网络请求
数据库操作
文件读写
SharedPreferences 读写
ContentProvider 查询

例子:

viewModelScope.launch {
    val result = withContext(Dispatchers.IO) {
        // 这里运行在 IO 线程
        api.getData()
    }

    // 这里又回到 Main 线程
    _uiState.value = result
}

也可以直接:

viewModelScope.launch(Dispatchers.IO) {
    // 整个协程默认运行在 IO 线程
    val result = api.getData()
}

但是如果后面要更新 UI,需要切回主线程:

viewModelScope.launch(Dispatchers.IO) {
    val result = api.getData()

    withContext(Dispatchers.Main) {
        // 回到主线程
        binding.textView.text = result.name
    }
}

2. CPU 计算:Dispatchers.Default

适合:

大量计算
排序
JSON 大量解析
图片压缩
加密解密
复杂算法

例子:

viewModelScope.launch {
    val result = withContext(Dispatchers.Default) {
        heavyCalculate()
    }

    // 回到主线程
    showResult(result)
}

4. launchasyncwithContext 分别是什么?

1. launch

启动一个协程,不返回结果。

viewModelScope.launch {
    // 默认 Main
}

指定线程:

viewModelScope.launch(Dispatchers.IO) {
    // IO 线程
}

适合做:

发起任务
更新状态
不需要直接返回值的任务

2. async

启动一个协程,会返回结果。

val deferred = viewModelScope.async(Dispatchers.IO) {
    api.getUser()
}

val user = deferred.await()

常用于并发请求:

viewModelScope.launch {
    val userDeferred = async(Dispatchers.IO) {
        api.getUser()
    }

    val orderDeferred = async(Dispatchers.IO) {
        api.getOrder()
    }

    val user = userDeferred.await()
    val order = orderDeferred.await()

    showData(user, order)
}

注意:

async 返回 Deferred
await() 获取结果

3. withContext

切换协程运行的线程,并且等待结果返回。

最常用。

val result = withContext(Dispatchers.IO) {
    api.getData()
}

它的特点:

切到指定线程执行
执行完再回到原来的协程上下文
可以返回结果

比如:

viewModelScope.launch {
    // Main 线程

    val data = withContext(Dispatchers.IO) {
        // IO 线程
        repository.loadData()
    }

    // 自动回到 Main 线程
    showData(data)
}

5. 最常见 Android 写法

推荐写法

viewModelScope.launch {
    // 主线程,可以控制 UI 状态
    _loading.value = true

    val data = withContext(Dispatchers.IO) {
        // 后台线程做耗时任务
        repository.getData()
    }

    // 回到主线程更新 UI 状态
    _loading.value = false
    _data.value = data
}

这个模式非常常见:

launch 默认 Main
withContext(IO) 做耗时任务
结束后回 Main 更新 UI

6. 对应关系总结

你想做什么 用什么
启动一个协程 launch {}
启动一个有返回值的协程 async {}
切换线程执行一段代码 withContext(...)
主线程执行 Dispatchers.Main
IO 任务 Dispatchers.IO
CPU 计算 Dispatchers.Default
获取 async 结果 await()
挂起等待但不阻塞线程 delay()

7. 默认在哪个线程,要看 Scope

这个非常重要。

viewModelScope.launch {}

viewModelScope.launch {
    // 默认 Main
}

lifecycleScope.launch {}

lifecycleScope.launch {
    // 默认 Main
}

CoroutineScope(Dispatchers.IO).launch {}

CoroutineScope(Dispatchers.IO).launch {
    // 默认 IO
}

runBlocking {}

runBlocking {
    // 阻塞当前线程
}

如果你在 main 函数里:

fun main() = runBlocking {
    println(Thread.currentThread().name)
}

它就在当前 main 线程。

如果你在 Android 主线程调用 runBlocking,会阻塞主线程,所以 Android 里一般不要用。


8. suspend 是不是代表切线程?

不是。

这个特别容易混。

suspend fun getData(): String {
    return "hello"
}

suspend 只表示:

这个函数可以挂起

不代表:

它一定在后台线程

也不代表:

它一定切线程

真正决定线程的是:

Dispatchers.IO
Dispatchers.Main
Dispatchers.Default

比如:

viewModelScope.launch {
    getData()
}

如果 launch 在 Main 线程,那么 getData() 默认也在 Main 线程执行,除非它内部自己切了线程。


9. 举个完整例子

viewModelScope.launch {
    println("1: ${Thread.currentThread().name}") 
    // Main

    val user = withContext(Dispatchers.IO) {
        println("2: ${Thread.currentThread().name}") 
        // IO 线程

        repository.getUser()
    }

    println("3: ${Thread.currentThread().name}") 
    // 回到 Main

    _user.value = user
}

执行逻辑:

1. viewModelScope.launch 默认在 Main
2. withContext(Dispatchers.IO) 切到 IO 线程
3. IO 执行完,自动回到 Main
4. 更新 UI 状态

10. 最后总结

你可以这样记:

launch:启动协程
async:启动协程并返回结果
withContext:切换调度器/线程
Dispatchers.Main:主线程
Dispatchers.IO:IO 线程池
Dispatchers.Default:计算线程池
suspend:可挂起,不代表切线程

最常用写法:

viewModelScope.launch {
    // Main 线程

    val result = withContext(Dispatchers.IO) {
        // IO 线程做耗时任务
        repository.loadData()
    }

    // 回到 Main 线程更新 UI
    updateUi(result)
}

一句话:

协程默认跑在哪,要看启动它的 Scope;要切线程,就用 withContext(Dispatchers.xxx) 或在 launch/async 里传入 Dispatchers.xxx

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇