Android Kotlin(7)之《协程2》
Android Kotlin第七篇 协程2。Kotlin系列源码在源码下载这里下载。我们一起来了解下Kotlin的协程,协程也是Kotlin重点。也许我有的地方没有写好,也欢迎大家提出问题,纠正问题。
当然协程目前还是实验性的,也就是说后续也许会有改动,应该改动不会太大,基本的都定了,不然就不会放出来了。
前面我们已经简单了解了协程,这篇我们来看下协程的取消超时与async
1、取消
当进行了一个非常耗时的操作,中途遇到问题需要中断操作,我们可以这样做:
fun test9() = runBlocking<Unit>{
var job = launch(CommonPool) {
repeat(1000) { i ->
log("$i ...")
delay(500L)
}
}
log("1")
delay(1500L)
log("job cancel")
job.cancel()
log("2")
}
输出:
08-03 13:32:30.469 3114-3154/com.xiaoqiang.kotlin I/test: 0 ...
08-03 13:32:30.469 3114-3114/com.xiaoqiang.kotlin I/test: 1
08-03 13:32:30.969 3114-3154/com.xiaoqiang.kotlin I/test: 1 ...
08-03 13:32:31.469 3114-3154/com.xiaoqiang.kotlin I/test: 2 ...
08-03 13:32:31.969 3114-3114/com.xiaoqiang.kotlin I/test: job cancel
08-03 13:32:31.969 3114-3114/com.xiaoqiang.kotlin I/test: 2
08-03 13:32:31.969 3114-3114/com.xiaoqiang.kotlin I/test: 结束
我可以看到输出结果,是不是我们想要的啊,当然也不是所有的Coroutine可以取消,如果一个程序在计算工作不检查取消,就不能取消,如下:
fun test10() = runBlocking<Unit>{
var job = launch(CommonPool) {
var i = 0
var nextPrintTime = System.currentTimeMillis()
while (i < 10) { // computation loop
val currentTime = System.currentTimeMillis()
if (currentTime >= nextPrintTime) {
log("I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
log("1")
delay(1500L)
log("job cancel")
job.cancel()
log("2")
}
你可以试着跑下输出看看,你会发现取消没有作用了。
那么如果你想取消计算代码,第一个是周期性地调用暂停函数,例如:
fun test11() = runBlocking<Unit>{
var job = launch(CommonPool) {
var i = 0
while (i < 10) { // computation loop
log("I'm sleeping ${i++} ...")
delay(500L)
}
}
log("1")
delay(1500L)
log("job cancel")
job.cancel()
log("2")
}
另一个是显式地检查(isActive)取消状态。让我们来试试后面的方法,例如:
fun test12() = runBlocking<Unit>{
var job = launch(CommonPool) {
var i = 0
var nextPrintTime = System.currentTimeMillis()
while (isActive) { // computation loop
val currentTime = System.currentTimeMillis()
if (currentTime >= nextPrintTime) {
log("I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
log("1")
delay(1500L)
log("job cancel")
job.cancel()
log("2")
}
这样我们就可以取消几乎所有的launch。
取消释放资源
如果就这样取消,那么我们如何关闭launch里资源呢,我们可以用try{}finally{}监听launch取消操作,在里面进行资源关闭,例如:
fun test13() = runBlocking<Unit> {
var job = launch(CommonPool) {
try {
repeat(1000) { i ->
log("$i ...")
delay(500L)
}
}finally {
//这里进行资源关闭
log("job close")
}
}
log("1")
delay(1500L)
log("job cancel")
job.cancel()
log("2")
delay(1000L)
}
如果你在取消coroutines时,需要长时间关闭资源,你可以用run{}来实现,例如:
fun test14() = runBlocking<Unit> {
var job = launch(CommonPool) {
try {
repeat(1000) { i ->
log("$i ...")
delay(500L)
}
}finally {
//这里进行资源关闭
run(NonCancellable) {
log("I'm running finally")
delay(1000L)
log("job close")
}
}
}
log("1")
delay(1500L)
log("job cancel")
job.cancel()
log("2")
delay(1000L)
}
2、超时
不想手动取消,那我们可以设置一个超时来自动取消,超时的时候回抛出TimeoutException异常,导致程序奔溃,你可以使用try{}catch(e:CancellationException){}捕获并进行想要的异常处理,例如:
fun test15() = runBlocking<Unit>{
try {
withTimeout(3000L){
repeat(1000) { i ->
log("$i ...")
delay(500L)
}
}
}catch (e:CancellationException){
log("CancellationException")
}
}
3、async
我们先来看一个协程的顺序执行:
suspend fun doSomethingUsefulOne(): Int {
log("one")
delay(1000L)
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
log("two")
delay(1000L)
return 29
}
//顺序执行
//measureTimeMillis:执行给定的块并以毫秒为单位返回运行时间。
fun test16() = runBlocking<Unit> {
val time = measureTimeMillis {
val one = doSomethingUsefulOne()
val two = doSomethingUsefulTwo()
log("one+two = ${one + two}")
}
log("耗时: $time ms")
}
输出:
08-03 14:37:17.426 13640-13640/? I/test: one
08-03 14:37:18.426 13640-13640/com.xiaoqiang.kotlin I/test: two
08-03 14:37:19.426 13640-13640/com.xiaoqiang.kotlin I/test: one+two = 42
08-03 14:37:19.426 13640-13640/com.xiaoqiang.kotlin I/test: 耗时: 2003 ms
08-03 14:37:19.426 13640-13640/com.xiaoqiang.kotlin I/test: 结束
那么我们想他们同步执行呢,也许你会想到前面我们学到的launch,launch是可以做到同步执行,但是你无法得到其运行的结果啊,这里Kotlin就提供了一个非常好用的异步async,async不只是能让其异步运行我们还能得到其返回值呢。
async与launch虽然都有异步的作用
返回不同:launch返回的是对其引用,async是用其未来的值作为返回。当然async也包含了launch的一些功能(join/cancel等等),例如:
fun test17() = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async(CommonPool){ doSomethingUsefulOne() }
val two = async(CommonPool){ doSomethingUsefulTwo() }
log("one+two = ${one.await() + two.await()}")
}
log("耗时: $time ms")
}
输出:
08-03 14:39:58.856 16861-16906/com.xiaoqiang.kotlin I/test: two
08-03 14:39:58.856 16861-16905/com.xiaoqiang.kotlin I/test: one
08-03 14:39:59.856 16861-16861/com.xiaoqiang.kotlin I/test: one+two = 42
08-03 14:39:59.856 16861-16861/com.xiaoqiang.kotlin I/test: 耗时: 1008 ms
08-03 14:39:59.856 16861-16861/com.xiaoqiang.kotlin I/test: 结束
懒加载(LAZY)
假设有一个异步需求并不是要立即生效,我想写好异步,然后在我需要做的时候在做,这时候我们就需要用到懒加载,当你需要等待结果的时候,才开始执行,例如:
fun test18() = runBlocking<Unit> {
val time = measureTimeMillis {
log("1")
val one = async(CommonPool,CoroutineStart.LAZY){ doSomethingUsefulOne() }
val two = async(CommonPool,CoroutineStart.LAZY){ doSomethingUsefulTwo() }
log("2")
log("one+two = ${one.await() + two.await()}")
}
log("耗时: $time ms")
}
输出:
08-03 14:43:43.966 20964-20964/com.xiaoqiang.kotlin I/test: 1
08-03 14:43:43.966 20964-20964/com.xiaoqiang.kotlin I/test: 2
08-03 14:43:43.966 20964-21012/com.xiaoqiang.kotlin I/test: one
08-03 14:43:44.976 20964-21012/com.xiaoqiang.kotlin I/test: two
08-03 14:43:45.976 20964-20964/com.xiaoqiang.kotlin I/test: one+two = 42
08-03 14:43:45.976 20964-20964/com.xiaoqiang.kotlin I/test: 耗时: 2002 ms
08-03 14:43:45.976 20964-20964/com.xiaoqiang.kotlin I/test: 结束
在这里你会发现每次异步async调用方法都要写async(){},如此是不是感觉很麻烦,我们可以进一步封装,例如:
fun asyncSomethingUsefulOne() = async(CommonPool) {
doSomethingUsefulOne()
}
fun asyncSomethingUsefulTwo() = async(CommonPool) {
doSomethingUsefulTwo()
}
fun test19() = runBlocking<Unit> {
val time = measureTimeMillis {
val one = asyncSomethingUsefulOne()
val two = asyncSomethingUsefulTwo()
log("one+two = ${one.await() + two.await()}")
}
log("耗时: $time ms")
}
因为这里asyncSomethingUsefulOne与asyncSomethingUsefulTwo不是suspend函数,所有你还可以这样做,结果与上面的一样:
fun test20(){
val time = measureTimeMillis {
val one = asyncSomethingUsefulOne()
val two = asyncSomethingUsefulTwo()
runBlocking {
log("one+two = ${one.await() + two.await()}")
}
}
log("耗时: $time ms")
}
到此我们对协程的认识更加多了,有没有爱上Kotlin,我是非常喜欢Kotlin,真的比java代码少,而且功能更加强大,易读。
全套源码下载这里源码会随着后面发布的Kotlin逐渐完善