kotlinkotlin频道Kotlin技术知识

Kotlin协程(coroutines)

2017-04-11  本文已影响6501人  747a945a4501

前言


今天看了下Kotlin的协程,觉得挺好的,写篇文章总结下。

理论


它是什么

这是别人翻译: 协程把异步编程放入库中来简化这类操作。程序逻辑在协程中顺序表述,而底层的库会将其转换为异步操作。库会将相关的用户代码打包成回调,订阅相关事件,调度其执行到不同的线程(甚至不同的机器),而代码依然想顺序执行那么简单。
我的理解:子任务程协作运行,优雅的处理异步问题解决方案。

它能干什么?

我在做安卓开发,它能替换掉Handler,AsyncTask 甚至是Rxjava来优雅的解决异步问题。

Paste_Image.png

如果不用协程,常规的写法是利用回调函数,不断处理异步的方法,bc调度的过程可能还需要线程切换。

var a返回值 = A任务()
B任务(a返回值){ b返回值 ->
  //回调方法
  C任务(b返回值){ c返回值 ->
      //回调方法
      D任务(c返回值)
  }
}

如果用协程方式,就会简单一些,代码上是顺序写,执行的过程由调度器去控制

  协程{
    D任务( C任务( B任务( A任务() ) ) )
  }

配置


那怎么在我们的项目中使用协程呢?
Gradle配置传送门

成员介绍


在使用之前,咱们要先了解下面几个类,
(为了帮助理解,我用角色的方式来说,咱们的程序相当于一家公司。)

CoroutineContext
字面上理解是协程上下文,说白了就是代码块执行在哪个场景。它就相当于部门团队的抽象定义

CoroutineDispatcher
调度器,由它来调度和处理任务。它是具体的团队,团队有处理任务的能力,所以有如下的抽象方法:

将代码块block运行在这个线程里面

他有几个实现类:

//它里面定一个线程池,去处理你的代码块
CommonPool
//用android的handler处理你的代码块,构造方法需要提供Handler
HandlerContext
//无限制,说白了就是当前什么线程就是什么线程。
Unconfined

//安卓开发用的 
val UI = HandlerContext(Handler(Looper.getMainLooper()), "UI")

Job
就是上面提到的任务,咱们要执行的过程被封装成Job,交给调度器执行。它是个接口
当然任务会有一些状态(生命周期),在它的源码注释里面都有解释

Paste_Image.png

可能随着业务需求,任务又需要扩展,不光可以执行,还可以取消,于是就是有Deferred,(它是job的子类)

Paste_Image.png
他有两个实现类
//任务创建后会立即启动
DeferredCoroutine
//任务创建后new的状态,要等用户调用 start() or join() or await()去启动他
LazyDeferredCoroutine

代码使用


从上面的介绍可以看出,启动一个协程需要调度器和任务,任务咱们知道,其实就是代码块或者方法,怎么转换为Job对象呢?

首先要说到一个关键字suspend,这个关键字说白了就是:标识这个函数只能被协程调用。

suspend fun doSomething(foo: Foo): Bar{
 ... 
}

api已经提供了方法来创建执行一个协程

public fun <T> async(context: CoroutineContext, start: Boolean = true, block: suspend CoroutineScope.() -> T) : Deferred<T> {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start)
        DeferredCoroutine<T>(newContext, active = true) else
        LazyDeferredCoroutine(newContext, block)
    coroutine.initParentJob(context[Job])
    if (start) block.startCoroutine(coroutine, coroutine)
    return coroutine
}


fun launch(context: CoroutineContext, start: Boolean = true, block: suspend CoroutineScope.() -> Unit): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start)
        StandaloneCoroutine(newContext, active = true) else
        LazyStandaloneCoroutine(newContext, block)
    coroutine.initParentJob(context[Job])
    if (start) block.startCoroutine(coroutine, coroutine)
    return coroutine
}

两个方法差不多,都是启动一个协程,不同的是 返回值,正如它们方法名一样,async是异步,可以取消。launch就是启动一个协程。
安卓常规的写法如下

val 异步任务 = async(CommonPool,false) {
    //需要线程中执行的代码块 
}
//上面的异步任务 参数false 说明需要调用 await()方法区启动这个任务
launch(UI) {  val 返回值 = 异步任务.await() }

//launch 默认参数 start= true 所以会立即执行

具体使用的例子,如下是我的代码:

//往数据库插入10条数据,然后界面提示
fun insertDBData() {
        val addJob = async(CommonPool,false) {
            val arrays = mutableListOf<DBBo>()
            for(i in 1..10){ random {
                arrays.add(DBBo("id$i${it.nextInt(1000)}",it.nextInt(1000),"info${it.nextInt(1000)}"))
            } }
            kApplication.dbOpt!!.getDao(DBBo::class.java).replaceObjs(arrays)
        }
        launch(UI) {
            showComfirmCrouton("${addJob.await()}条数据添加成功!")
        }
    }

如果都是这个模式调用(线程和主线程优化),代码可以在优雅下去:

fun <T> coroutine(block: suspend CoroutineScope.() -> T , uiBlock: suspend (T) -> Unit):Deferred<T> {
        val deferred = async(CommonPool,false,block)
        launch(UI){
            uiBlock(deferred.await())
        }
        return deferred
    }

这样上面的代码可以优化成:

coroutine({
  val arrays = mutableListOf<DBBo>()
  for(i in 1..10){ random {
     arrays.add(DBBo("id$i${it.nextInt(1000)}",it.nextInt(1000),"info${it.nextInt(1000)}"))
            } }
  kApplication.dbOpt!!.getDao(DBBo::class.java).replaceObjs(arrays)
  }){
    showComfirmCrouton("${it}条数据添加成功!")
  }

OK,就写到这,下面开始修改自己封装的框架。

上一篇下一篇

猜你喜欢

热点阅读