为什么用协程
Android 主线程不能做耗时操作(网络、IO),以前用回调、RxJava,代码很容易变成”回调地狱”。Kotlin 协程用近乎同步的写法处理异步,并且能在 Activity 销毁时自动取消,天然防止内存泄漏。
一、基础概念
1 | // 在协程里请求网络数据 |
三个关键词:
launch:启动一个协程,不等待结果;async:启动协程并返回Deferred,可以await()拿结果;suspend:标记挂起函数,只能在协程里调用。
二、Dispatchers:线程切换
1 | viewModelScope.launch(Dispatchers.IO) { // IO 线程池,适合网络/文件 |
常用调度器:
| Dispatcher | 用途 |
|---|---|
Dispatchers.Main |
主线程,更新 UI |
Dispatchers.IO |
网络、磁盘 IO(线程池) |
Dispatchers.Default |
CPU 密集计算 |
Dispatchers.Unconfined |
不限制,一般不用 |
记住:网络/IO 用 Dispatchers.IO,更新 UI 用 Dispatchers.Main。用 withContext 而不是手动 GlobalScope 切线程。
三、结构化并发:作用域是核心
协程最重要的一点是”结构化并发“:协程必须属于某个作用域(Scope),作用域取消时,它下面的所有子协程一起取消。
1 | // 错误示范:GlobalScope 不受生命周期控制 |
Android 生态里直接使用现成作用域:
viewModelScope:ViewModel 销毁时自动取消lifecycleScope:Activity/Fragment 销毁时自动取消repeatOnLifecycle:只在页面可见时执行
四、async/await:并行请求
1 | viewModelScope.launch { |
串行(慢)和并行(快)的选择:无依赖请求用 async 并行,有依赖的用 await 串联。
五、异常处理
1 | viewModelScope.launch { |
注意 SupervisorJob 和 Job 的区别:SupervisorJob 中子协程失败不会级联取消兄弟协程,适合 UI 场景;Job 会级联取消。
1 | // UI 场景推荐:一个任务失败不影响其他任务 |
六、Flow:协程版的响应式数据流
需要持续产生数据(如监听数据库、输入框防抖)用 Flow:
1 | class UserRepository { |
Flow 天然支持协程取消:作用域销毁,收集自动停止。
常见坑
- 在
launch外直接调用suspend函数:编译报错,suspend 函数只能在协程作用域调用; - 忘记
withContext(Dispatchers.Main)更新 UI:在 IO 线程改 UI 会崩溃; - 用
GlobalScope写业务:几乎都是错的,脱离生命周期的协程 = 内存泄漏风险; async没await:协程照样跑,只是没人等结果,浪费资源。
总结
协程上手三步:viewModelScope.launch 起协程 → 网络/IO 用 Dispatchers.IO + withContext(Main) 更新 UI → 无依赖请求用 async 并行。把”作用域跟着生命周期走”这个原则记住,协程就是最顺手的异步方案。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 JCAlways!
评论
