Kotlin语言基础入门到熟悉:Kotlin协程基础
1. 阻塞与非阻塞
runBlocking
delay是非阻塞的,Thread.sleep是阻塞的。显式使用runBlocking协程构建器来阻塞。
importkotlinx.coroutines.\*funmain(){GlobalScope.launch{// 在后台启动一个新的协程并继续delay(200)"rustfisher.com".forEach{print(it)delay(280)}}println("主线程中的代码会立即执行")runBlocking{// 这个表达式阻塞了主线程delay(3000L)//阻塞主线程防止过快退出}println("\n示例结束")}可以看到,runBlocking里使用了delay来延迟。用了runBlocking的主线程会一直阻塞直到runBlocking内部的协程执行完毕。 也就是runBlocking{ delay }实现了阻塞的效果。
我们也可以用runBlocking来包装主函数。
importkotlinx.coroutines.\*funmain()=runBlocking{delay(100)// 在这里可以用delay了GlobalScope.launch{delay(100)println("Fisher")}print("Rust ")delay(3000)}runBlocking<Unit>中的<Unit>目前可以省略。
runBlocking也可用在测试中
// 引入junitdependencies{implementation("junit:junit:4.13.1")}单元测试
使用@Test设置测试
importorg.junit.Testimportkotlinx.coroutines.\*classC3Test{@Test funtest1()=runBlocking{println("junit测试开始 ${System.currentTimeMillis()}")delay(1234)println("junit测试结束 ${System.currentTimeMillis()}")}}运行结果
junit测试开始1632401800686junit测试结束1632401801928IDEA可能会提示no tasks available。需要把测试选项改为IDEA,如下图。
更改设置
2. 等待
有时候需要等待协程执行完毕。可以用join()方法。这个方法会暂停当前的协程,直到执行完毕。需要用main() = runBlocking。
importkotlinx.coroutines.\*funmain()=runBlocking{println("测试等待")val job1=GlobalScope.launch{println("job1 start")delay(300)println("job1 done")}val job2=GlobalScope.launch{println("job2 start")delay(800)println("job2 done")}job2.join()job1.join()// 等待println("测试结束")}运行log
测试等待 job1 start job2 start job1 done job2 done 测试结束3. 结构化的并发
用GlobalScope.launch时,会创建一个顶层协程。它不使用主线程。新创的协程虽然轻量,但仍会消耗一些内存资源。如果忘记保持对新启动的协程的引用,它还会继续运行。
我们可以在代码中使用结构化并发。
示例中,我们使用runBlocking协程构建器将main函数转换为协程。在里面(作用域)启动的协程不需显式使用join。
观察下面的例子:
importkotlinx.coroutines.\*funmain()=runBlocking<Unit>{println("主线程id ${Thread.currentThread().id}")launch{// 在 runBlocking 作用域中启动一个新协程1println("协程1所在线程id ${Thread.currentThread().id}")delay(300)println("协程1执行完毕")}launch{// 在 runBlocking 作用域中启动一个新协程2println("协程2所在线程id ${Thread.currentThread().id}")delay(500)println("协程2执行完毕")}println("主线程执行完毕")}运行log
主线程id1主线程执行完毕 协程1所在线程id1协程2所在线程id1协程1执行完毕 协程2执行完毕可以看到,不用像之前那样调用Thread.sleep或者delay让主线程等待一段时间,防止虚拟机退出。
程序会等待它所有的协程执行完毕,然后真正退出。
4. 作用域构建器
使用coroutineScope构建器声明自己的作用域。它会创建一个协程作用域,并且会等待所有已启动子协程执行完毕。
runBlocking与coroutineScope看起来类似,因为它们都会等待其协程体以及所有子协程结束。主要区别在于:
*runBlocking方法会阻塞当前线程来等待,是常规函数
*coroutineScope只是挂起,会释放底层线程用于其他用途,是挂起函数
下面这个示例展示了作用域构建器的特点。main是一个作用域。
importkotlinx.coroutines.\*funmain()=runBlocking{// this: CoroutineScopelaunch{delay(200L)println("协程1 t${Thread.currentThread().id}")}coroutineScope{// 创建一个协程作用域launch{delay(500L)println("内部协程2-1 t${Thread.currentThread().id}")}delay(100L)println("协程2 t${Thread.currentThread().id}")}println("主任务完毕")}运行log
协程2t1 协程1t1 内部协程2-1t1 主任务完毕5. 提取函数重构
将launch { …… }内部的代码块提取到独立的函数中。提取出来的函数需要suspend修饰符,它是挂起函数。
importkotlinx.coroutines.delayimportkotlinx.coroutines.launchimportkotlinx.coroutines.runBlocking funmain()=runBlocking<Unit>{launch{r1()}println("DONE")}// 挂起函数suspend funr1(){delay(300)println("提取出来的函数")}log
DONE提取出来的函数6. 协程是轻量的
我们前面也试过,创建非常多的协程,程序运行OK。
下面的代码可以输出很多的点
importkotlinx.coroutines.\*funmain()=runBlocking{for(tin1..10000){launch{delay(t \*500L)print(".")}}}7. 全局协程像守护线程
如果进程中只剩下了守护线程,那么虚拟机会退出。 前文那个的例子,其实也能看到,字符没打印完程序就结束了。
在GlobalScope中启动的活动协程并不会使进程保活。它们就像守护线程。
再举一个例子
importkotlinx.coroutines.\*funmain()=runBlocking{GlobalScope.launch{for(iin1..1000000){delay(200)println("协程执行: $i")}}delay(1000)println("Bye~")}log
协程执行:1协程执行:2协程执行:3协程执行:4Bye~最后分享一份
【腾讯技术团队出品】Android零基础入门到精通,Android Studio安装教程+全套安卓基础教程
Android编程入门教程
Java语言基础从入门到熟悉
Kotlin语言基础从入门到熟悉
Android 技术栈从入门到熟悉
Android Jetpack 全家桶全面学习
对于新手来说可能安装Android Studio存在一定困难你可以看着以下视频,一步步的跟着学习安装运行
Android Studio 安装教程
有了Java阶段的学习,这一阶段建议以视频学习为主辅以图书查漏补缺。如果以图书为主,可以根据图书讲解敲代码,辅以教学视频查漏补缺。遇到问题可以去百度,入门的问题一般会有很多人遇到,并且给出比较好的解答。
需要掌握基本知识点,比如四大组件如何使用、如何创建Service、如何进行布局、简单的自定义View、动画、网络通信等常见技术。
全套零基础教程已经为你们准备好了,需要的可以添加下方二维码免费领取
全套安卓基础教程