Kotlin协程笔记
引入携程所需库
第一步:项目级build.gradle中
buildscript { ext.kotlin_coroutines = '1.3.1' }
第二步:Module级别build.gradle中
dependencies { // 👇 依赖协程核心库 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines" // 👇 依赖当前平台所对应的平台库,如Android或js implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines" //协程的声明周期库,该库可以选加,是让协程的生命周期和Activity的周期保持一致 implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha02' }
注:核心库和平台库版本需要保持一致
协程是什么
- 协程:是一个线程框架,最核心的部分是【非阻塞式】和【挂起】;就是launch{协程部分}async{协程部分}
相关名词
1、GloabScope:全局范围
2、Coroutine:协程
3、suspend:挂起、暂停
- a、代码执行到用suspend标识的函数时会挂起,且这种挂起是不会阻塞的,不会影响当前线程的执行,而这种所谓的挂起,其实就是切一个线程,且在函数执行完毕会自动将线程切回来的调度工作。
- b、用suspend标识的函数只能运行在协程中;或者被另一个被suspend标识的函数调用,其实最终还是运行在一个协程中。
- c、suspend本身是不能真正实现挂起的,他的作用主要是一个提醒,是创建者对使用者的提醒,该函数是一个耗时函数,且是用挂起的方式在后台运行,所以请在协程中调用我
- d、suspend的意义:传递CoroutineContext
相关方法介绍
runBlocking :一般用于 单元测试中,是线程阻塞的
launch(Dispatchers.IO){}:可以用来切线程
coroutineScope.launch(Dispatchers.IO) {
...
launch(Dispatchers.Main){
...
launch(Dispatchers.IO) {
...
launch(Dispatchers.Main) {
...
}
}
}
}
withContext(){}:切换线程,当需要频繁的进行线程切换时,因为可以自动切回原来的线程,所以相应的代码嵌套层次没那么多,并在闭包内的逻辑执行结束后自动切回原来的线程,继续执行
coroutineScope.launch(Dispatchers.Main) {
...
withContext(Dispatchers.IO) {
...
}
...
withContext(Dispatchers.IO) {
...
}
...
}
Async{}:返回的协程实现了Deferred。可以使用wait()方法
创建携程的方法
方法一:使用runBlocking 顶层函数,适用于单元测试的情况,是线程阻塞的,一般正常的业务开发不会使用
runBlocking{个人理解:此次相当于开启一个线程,但是会阻塞当前线程,相当于一个耗时任务??? getImage(imageId) }
方法二:使用GlobalScope 单例对象,直接调用launch开启协程,不会阻塞线程,但是实际开发中也一般不会使用,因为它的生命周期和APP的生命周期是一致的
GlobalScope.launch{ getImage(imageId) }
方法三:通过CoroutineContext创建一个CoroutineScope(接口)对象,需要一个类型为CoroutineContext的参数。开发中推荐这种方法,--coroutineContext?????
val coroutineScope = CoroutineScope(coroutineContext) coroutineScope.launch{ getImage(imageId) }
方法四:主要用于在一个协程中开启另一个协程,直接在普通代码中无法使用
launch{ }
方法五:该方法与launch类似,也是在协程中使用
async{ }
async 和 launch对比
相同点:都可以用来启动一个协程,返回的都是Coroutine
不同点:
async返回的Coroutine还实现了Deferred(意思是延迟,也就是结果稍后才能拿到。)接口。可以使用await暂停函数来返回result
launch可启动新协程而不将结果返回给调用方
async使用场景:如现在需要展示公司LOGO和个人头像,但是需要等待两者都返回了,才可以去显示,两者同时进行,然后等较慢的执行完毕就可以显示了,所花费的时间就是两个中较长的一个,而不是两者之和
coroutineScope.launch(Dispatchers.Main) {
// 👇 async 函数启动新的协程
val avatar: Deferred = async { api.getAvatar(user) } // 获取用户头像
val logo: Deferred = async { api.getCompanyLogo(user) } // 获取用户所在公司的 logo
// 👇 👇 获取返回值
show(avatar.await(), logo.await()) // 更新 UI
}
挂起的本质
什么是挂起:挂起就是稍后会自动切回来的操作
在协程中,我们挂起的对象既不是线程,也不是函数,而是我们挂起的对象是协程,就是launch和async函数中的闭包的代码块,而launch和async或者其他函数创建的协程在执行到某一个suspend函数的时候,这个函数就会被挂起,或者说从当前线程中脱离。
自定义suspend函数
-
什么时候需要自定义suspend函数
比如某个函数时比较函数的,可以将他协程suspend函数,这就是原则 耗时操作分为:I/O操作和CPU计算工作,比如文件读写,网络交互,图片的模糊处理。 另一种耗时操作:如事情本身并不慢,但是因为某些原因需要等5s或者特定时间再执行,这种也可以定义为suspend
-
具体如何实现自定义
1、给函数加上suspend关键字 2、用withContext将函数内容包住(方便执行完毕,自动切回原始线程) 3、针对需要等待的耗时操作,我们可以直接使用另一个挂起函数delay()