Android Kotlin回顾10.如何启动协程
Posted datian1234
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Kotlin回顾10.如何启动协程相关的知识,希望对你有一定的参考价值。
1.launch启动协程
fun main() = runBlocking
launch
delay(1000L)
println("World!")
println("Hello")
fun main()
GlobalScope.launch
delay(1000L)
println("World!")
println("Hello")
Thread.sleep(2000L)
//输出结果
//Hello
//World!
上面是两段代码,这两段代码都是通过launch
启动了一个协程并且输出结果也是一样的。
第一段代码中的runBlocking
是协程的另一种启动方式,这里先看第二段代码中的launch
的启动方式;
- GlobalScope.launch
GlobalScope.launch
是一个扩展函数,接收者是CoroutineScope
,意思就是协程作用域,这里的launch
等价于CoroutineScope
的成员方法,如果要调用launch
来启动一个协程就必须先拿到CoroutineScope
对象。GlobalScope.launch
源码如下
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
里面有三个参数:
-
context: 意思是上下文,默认是
EmptyCoroutineContext
,有默认值就可以不传,但是也可以传递Kotlin提供的Dispatchers
来指定协程运行在哪一个线程中; -
start:
CoroutineStart
代表了协程的启动模式,不传则默认使用DEFAULT(根据上下文立即调度协程执行)
,除DEFAULT
外还有其他类型: -
- LAZY:延迟启动协程,只在需要时才启动。
- ATOMIC:以一种不可取消的方式,根据其上下文安排执行的协程;
- UNDISPATCHED:立即执行协程,直到它在当前线程中的第一个挂起点;
-
block:
suspend
是挂起的意思,CoroutineScope.()
是一个扩展函数,Unit
是一个函数类似于Java的void
,那么suspend CoroutineScope.() -> Unit
就可以这么理解了:首先,它是一个挂起函数,然后它还是CoroutineScope
类的成员或者扩展函数,参数为空,返回值类型为Unit
。 -
delay(): delay()方法从字面理解就是延迟的意思,在上面的代码中延迟了1秒再执行World,从源码可以看出来它跟其他方法不一样,多了一个
suspend
关键字
// 挂起
// ↓
public suspend fun delay(timeMillis: Long)
if (timeMillis <= 0) return // don't delay
return suspendCancellableCoroutine sc@ cont: CancellableContinuation<Unit> ->
// if timeMillis == Long.MAX_VALUE then just wait forever like awaitCancellation, don't schedule.
if (timeMillis < Long.MAX_VALUE)
cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
suspend
的意思就是挂起,被它修饰的函数就是挂起函数, 这也就意味着delay()方法具有挂起和恢复的能力;
- Thread.sleep(2000L)
这个是休眠2秒,那么这里为什么要有这个呢?要解答这疑问其实不难,将Thread.sleep(2000L)
删除后在运行代码可以发现只打印了Hello
然后程序就结束了,World!
并没有被打印出来。
为什么? 将上面的代码转换成线程实现如下:
fun main()
thread(isDaemon = true)
Thread.sleep(1000L)
println("Hello World!")
如果不添加isDaemon = true
结果输出正常,如果加了那么就没有结果输出。isDaemon
的加入后其实是创建了一个【守护线程】,这就意味着主线程结束的时候它会跟着被销毁,所以对于将Thread.sleep
删除后导致GlobalScope
创建的协程不能正常运行的主要原因就是通过launch
创建的协程还没开始执行程序就结束了。那么Thread.sleep(2000L)
的作用就是为了不让主线程退出。
另外这里还有一点需要注意:程序的执行过程并不是按照顺序执行的。
fun main()
GlobalScope.launch // 1
println("Launch started!") // 2
delay(1000L) // 3
println("World!") // 4
println("Hello") // 5
Thread.sleep(2000L) // 6
println("Process end!") // 7
/*
输出结果:
Hello
Launch started!
World!
Process end!
*/
上面的代码执行顺序是1、5、6、2、3、4、7,这个其实好理解,首先执行1,然后再执行5,执行6的时候等待2秒,在这个等待过程中协程创建完毕了开始执行2、3、4都可以执行了,当2、3、4执行完毕后等待6执行完毕,最后执行7,程序结束。
2.runBlocking启动协程
fun main()
runBlocking // 1
println("launch started!") // 2
delay(1000L) // 3
println("World!") // 4
println("Hello") // 5
Thread.sleep(2000L) // 6
println("Process end!") // 7
上面这段代码只是将GlobalScope.launch
改成了runBlocking
,但是执行顺序却完全不一样,它的执行顺讯为代码顺序1~7,这是因为runBlocking
是带有阻塞属性的,它会阻塞当前线程的执行。这是它跟launch
的最大差异。
runBlocking
与lanuch
的另外一个差异是GlobalScope
,从代码中可以看出runBlocking
并不需要这个,这点可以从源码中分析
public actual fun <T> runBlocking(
context: CoroutineContext,
block: suspend CoroutineScope.() -> T): T
...
顶层函数:类似于Java中的静态函数,在Java中常用与工具类,例如StringUtils.lastElement();
runBlocking
是一个顶层函数,因此可以直接使用它;在它的第二个参数block
中有一个返回值类型:T,它刚好跟runBlocking
的返回值类型是一样的,因此可以推测出runBlocking
是可以有返回值的
fun main()
val result = test(1)
println("result:$result")
fun test(num: Int) = runBlocking
return@runBlocking num.toString()
//输出结果:
//result:1
但是,Kotlin在文档中注明了这个函数不应该从协程中使用。它的设计目的是将常规的阻塞代码与以挂起风格编写的库连接起来,以便在主函数和测试中使用。 因此在正式环境中这种方式最好不用。
3.async启动协程
在 Kotlin 当中,可以使用 async 创建协程,并且还能通过它返回的句柄拿到协程的执行结果。
fun main() = runBlocking
val deferred = async
1 + 1
println("result:$deferred.await()")
//输出结果:
//result:2
上面的代码启动了两个协程,启动方式是runBlocking
和async
,因为async
的调用需要一个作用域,而runBlocking
恰好满足这个条件,GlobalScope.launch
也可以满足这个条件但是GlobalScope
也不建议在生产环境中使用,因为GlobalScope
创建的协程没有父协程,GlobalScope
通常也不与任何生命周期组件绑定。除非手动管理,否则很难满足我们实际开发中的需求。
上面的代码多了一个deferred.await()
它就是获取最终结果的关键。
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine<T>(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
async
和launch
一样也是一个扩展函数,也有三个参数,和launch
的区别在于两点:
- block的函数类型:
launch
返回的是Unit
类型,async
返回的是泛型T
- 返回值不同:
launch
返回的是Job
,async
返回的是Deffered<T>
,而async
可以返回执行结果的关键就在这里。
启动协程的三种方式都讲完了,这里存在一个疑问,launch
和async
都有返回值,为什么async
可以获取执行结果,launch
却不行?
这主要跟launch
的返回值有关,launch
的返回值Job
代表的是协程的句柄,而句柄并不能返回协程的执行结果。
句柄: 句柄指的是中间媒介,通过这个中间媒介可以控制、操作某样东西。举个例子,door handle 是指门把手,通过门把手可以去控制门,但 door handle 并非 door 本身,只是一个中间媒介。又比如 knife handle 是刀柄,通过刀柄可以使用刀。
协程的三中启动方式区别如下:
- launch:无法获取执行结果,返回类型Job,不会阻塞;
- async:可获取执行结果,返回类型Deferred,调用await()会阻塞不调用则不会但也无法获取执行结果;
- runBlocking:可获取执行结果,阻塞当前线程的执行,多用于Demo、测试,官方推荐只用于连接线程与协程。
作者:无糖可乐爱好者
链接:https://juejin.cn/post/7171981069720223751
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
一、架构师筑基必备技能
1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……
二、Android百大框架源码解析
1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程
三、Android性能优化实战解析
- 腾讯Bugly:对字符串匹配算法的一点理解
- 爱奇艺:安卓APP崩溃捕获方案——xCrash
- 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
- 百度APP技术:Android H5首屏优化实践
- 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
- 携程:从智行 Android 项目看组件化架构实践
- 网易新闻构建优化:如何让你的构建速度“势如闪电”?
- …
四、高级kotlin强化实战
1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》
-
从一个膜拜大神的 Demo 开始
-
Kotlin 写 Gradle 脚本是一种什么体验?
-
Kotlin 编程的三重境界
-
Kotlin 高阶函数
-
Kotlin 泛型
-
Kotlin 扩展
-
Kotlin 委托
-
协程“不为人知”的调试技巧
-
图解协程:suspend
五、Android高级UI开源框架进阶解密
1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
六、NDK模块开发
1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习
七、Flutter技术进阶
1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)
…
八、微信小程序开发
1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……
全套视频资料:
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓
以上是关于Android Kotlin回顾10.如何启动协程的主要内容,如果未能解决你的问题,请参考以下文章