Kotlin学习kotlinKotlin专题

Android Kotlin(7)之《协程2》

2017-08-03  本文已影响0人  小强彬

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逐渐完善

上一篇下一篇

猜你喜欢

热点阅读