文章目录
虽然学习了Kotlin的协程,但总忘记怎么使用了,因此简单的记录于此,方便自己查阅和回顾。
在开发程序时会遇到一些耗时的操作,如网络IO、文件IO、CPU/GPU密集型任务等,这些操作会阻塞线程直到操作完成。线程阻塞问题,在Kotlin中除了通过开启新线程来解决外,还可以使用协程来解决。
正文
协程和线程
| 特性 | 协程 (Coroutines) | 传统线程 (Threads) |
|---|---|---|
| 资源消耗 | 极低(单线程可运行大量协程) | 较高(每个线程需要约1MB内存) |
| 切换开销 | 小(在用户态由程序控制,无需内核介入) | 大(需要系统内核进行线程调度) |
| 编码风格 | 同步式顺序编码,避免回调地狱 | 常需回调或复杂同步机制,易产生嵌套 |
| 生命周期管理 | 提供结构化并发,可轻松绑定到组件生命周期 | 通常需手动管理,相对复杂 |
协成
协程主要特点:
协程挂起:协程提供了一种使程序避免阻塞且更廉价可控的操作,叫作协程挂起,协程挂起不阻塞线程
简化代码:协程让原来使用“异步+回调”方式写出来的复杂代码,简化成可以用看似同步的方式表达
依赖
build.gradle文件中添加依赖
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") //Android项目 implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
启动
在Kotlin程序中,启动一个协程的方式有很多种,例如通过launch()函数、runBlocking()函数等方式来启动一个协程,最常用的方式是通过launch()函数来启动协程。
lifecycleScope
需要再AppCompatActivity类中
lifecycleScope.launch { Log.d(TAG, "launch 1 : ") }
运行在主线程中
runBlocking
runBlocking { Log.d(TAG, "launch 2 : ") }
运行在子线程
coroutineScope
suspend fun suspendTest() { coroutineScope { launch { //略 } } }
有suspend标志
Suspend function 'suspend fun suspendTest(): Unit' can only be called from a coroutine or another suspend function.
执行在协成中
runBlocking { suspendTest() }
CoroutineScope
CoroutineScope(Dispatchers.IO).launch { Log.d(TAG, "launch 2 : ") }
其中参数还可以有如下
1. Default 2. Main 3. IO 4. Unconfined
根据上面参数而定
Default 默认子线程
Main 主线程
IO 子线程
Unconfined 根据当前运行环境。如果在主线程找那个调用,运行在主线程;反之,子线程。
GlobalScope
GlobalScope.launch { // }
运行在子线程。
不推荐使用 GlobalScope启动协程,因为 GlobalScope 启动的协程生命周期与应用程序的生命周期一致,无法取消。
MainScope
官方建议在Android中自定义协程作用域,当然Kotlin提供了MainScope,让我们直接使用。
public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)
class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() { //略 }
使用launch或async
launch { //main线程 val test = async { //DefaultDispatcher-worker-1 工作线程 delay(1000) "test" } }
最后别忘了,在 Activity onDestory 时取消协程。
override fun onDestroy() {
cancel()
super.onDestroy()
}
ViewModelScope
如果你使用了 ViewModel + LiveData 实现 MVVM 架构,根本就不会在 Activity 上书写任何逻辑代码,更别说启动协程了。这个时候大部分工作就要交给 ViewModel 了。那么如何在 ViewModel 中定义协程作用域呢?
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.launch class ViewModelOne : ViewModel() { private val viewModelJob = SupervisorJob() private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob) val mMessage: MutableLiveData<String> = MutableLiveData() fun getMessage(message: String) { uiScope.launch { val deferred = async(Dispatchers.IO) { delay(2000) "post $message" } mMessage.value = deferred.await() } } override fun onCleared() { super.onCleared() viewModelJob.cancel() } }
区别
简单对比一下常用的几个之间的区别
| 特性 | CoroutineScope() | GlobalScope | runBlocking | coroutineScope |
|---|---|---|---|---|
| 作用域大小 | 受scope限制 | 全局 | 当前线程 | 当前协程 |
| 线程阻塞 | 否 | 否 | 是 | 否 |
| 自动取消 | 是 | 否 | 是 | 是 |
| 适用场景 | 组件生命周期管理 | 全局任务 | 测试 | suspend函数 |
参考文章
部分内容来源如下
《》
《》
历史上的今天
- 《Kotlin中使用协程简单记录》
- 《Kotlin之子类和子类型》
- 《久久韩剧网》
- 《饭团影视》
- 《网易娱乐》
- 《神马电影网》
- 《影视之家》
- 《蓝光影院》
- 《看看电影》
