目录
引入kotlin协程
kotlin协程的三种启动方式
初识协程:
第一种启动方式(runBlocking:T)
第二种启动方式(launch:Job)
第三种启动方式(async/await:Deferred)
实现你的第一个Coroutine程序
浅析Kotlin协程 ——协程是什么?https://blog.csdn.net/qq_17798399/article/details/95234416
在Android module中的build.gradle的dependencies中添加依赖。
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"
1.runBlocking:T
2.launch:Job
3.async/await:Deferred
随后再详细介绍三种启动方式
首先我们来看一眼协程是什么样的, 以下引用了官网的一个例子:
fun main(args: Array) {
launch(CommonPool) {
delay(1000L)
println("World!")
}
println("Hello,")
Thread.sleep(2000L)
}
/*
运行结果: ("Hello,"会立即被打印, 1000毫秒之后, "World!"会被打印)
Hello,
World!
*/
运行流程:
A. 主流程:
B. 协程流程:
解释一下delay方法:
在协程里delay方法作用等同于线程里的sleep, 都是休息一段时间, 但不同的是delay不会阻塞当前线程, 而像是设置了一个闹钟, 在闹钟未响之前, 运行该协程的线程可以被安排做了别的事情, 当闹钟响起时, 协程就会恢复运行.
协程启动后还可以取消
launch方法有一个返回值, 类型是Job, Job有一个cancel
方法, 调用cancel方法可以取消协程, 看一个数羊的例子:
fun main(args: Array) {
val job = launch(CommonPool) {
var i = 1
while(true) {
println("$i little sheep")
++i
delay(500L) // 每半秒数一只, 一秒可以输两只
}
}
Thread.sleep(1000L) // 在主线程睡眠期间, 协程里已经数了两只羊
job.cancel() // 协程才数了两只羊, 就被取消了
Thread.sleep(1000L)
println("main process finished.")
}
运行结果是:
1 little sheep
2 little sheep
main process finished.
如果不调用cancel, 可以数到4只羊.
runBlocking 方法用于启动一个协程任务,通常只用于启动最外层的协程,例如线程环境切换到协程环境。
runBlocking启动的协程任务会阻断当前线程,直到该协程执行结束。
执行结果:可以清楚的看到先将协程中的任务完成才执行主线程中的逻辑
我们最常用的用于启动协程的方式,它最终返回一个Job类型的对象,这个Job类型的对象实际上是一个接口,它包涵了许多我们常用的方法。例如join()启动一个协程、cancel() 取消一个协程
该方式启动的协程任务是不会阻塞线程的
执行结果:可以清楚的看到主线程没有被阻塞
1.async和await是两个函数,这两个函数在我们使用过程中一般都是成对出现的。
2.async用于启动一个异步的协程任务,await用于去得到协程任务结束时返回的结果,结果是通过一个Deferred对象返回的。
执行结果:可以看到当协程任务执行完毕时可以通过await()拿到返回结果
现在我们来开始编写我们的第一个Coroutine例子程序,这个程序的主要功能就是从手机媒体中加载一张图片,并把它显示在一个ImageView中。我们先来看看在未使用Coroutine之前使用同步的方式加载图片的代码如下:
val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
imageView.setImageBitmap(bitmap)
在上边的代码中我们从媒体读取了一张图片并把它转化成Bitmap对象。因为这是一个IO操作,如果我们在UI主线程中调用这段代码,将可能导致程序卡顿或产生ANR崩溃,所以我们需要在新开的线程中调用下边的代码
val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
接着我们需要在UI线程中调用下边的代码来显示加载的图片
imageView.setImageBitmap(bitmap)
为了实现这一功能在传统的android程序中我们需要使用Handler或AsyncTask将结果从非UI主线程发送到UI主线程进行显示,我们需要编写许多额外的代码。并且这些代码的可读性也不是十分的友好。下边我们来看看使用Kotlin的Coroutine来实现图片的加载的代码,如下:
val job = launch(Background) {
val bitmap = MediaStore.Images.Media.getBitmap(contentResolver,uri)
launch(UI) {
imageView.setImageBitmap(bitmap)
}
}
我们先忽略返回值job,我们稍后会进行介绍,在这儿我们关心的事情是launch函数和参数Background与UI。与之前使用同步的方式加载图片相比唯一的不同就在于这儿我们调用了lauch函数。lauch()创建并启动了一个协程,这儿的参数Background是一个CoroutineContext对象,确保这个协程运行在一个后台线程,确保你的应用程序不会因耗时操作而阻塞和崩溃。你可以像下边这样定义一个CoroutineContext:
internal val Background = newFixedThreadPoolContext(2, "bg")
他将使用含有两个线程的线程池来执行协程里边的操作。在第一个协程里边我们又调用了launch(UI)创建并启动了一个新的协程,这儿的UI并不是我们自己创建的,他是Kotlin在Android平台里边预定义的一个CoroutineContext,代表着在UI主线程中执行协程里边的操作。所以我们将更新程序界面的操作imageView.setImageBitmap(bitmap)放在了这个协程里。通过这儿的例子代码你会发现在kotlin里边使用协程来实现线程间的通信和切换非常的简单,比RxJava还简单。看上去就跟你写同步的方式的代码一样。
取消协程
在上边的例子中我们返回了一个Job类型的对象job。通过调用job.cancel()我们能够取消一个协程。例如当我们退出当前Activity的时候,图片还没有加载完。这个时候我们就可以在onDestroy中调用job.cancel()来取消这个未完成的任务。这与我们使用Rxjava时调用dipose()或使用AsyncTask时调用cancel() 来取消未完成的操作的作用是一样的。
参考文章:
https://www.jianshu.com/p/04f28bbc66dc
https://www.jianshu.com/p/9f720b9ccdea
https://www.jianshu.com/p/b6fb75cfaf5a