Kotlin协程(coroutines)
前言
今天看了下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:
调度器,由它来调度和处理任务。它是具体的团队,团队有处理任务的能力,所以有如下的抽象方法:
他有几个实现类:
//它里面定一个线程池,去处理你的代码块
CommonPool
//用android的handler处理你的代码块,构造方法需要提供Handler
HandlerContext
//无限制,说白了就是当前什么线程就是什么线程。
Unconfined
//安卓开发用的
val UI = HandlerContext(Handler(Looper.getMainLooper()), "UI")
Job
就是上面提到的任务,咱们要执行的过程被封装成Job,交给调度器执行。它是个接口
当然任务会有一些状态(生命周期),在它的源码注释里面都有解释
可能随着业务需求,任务又需要扩展,不光可以执行,还可以取消,于是就是有Deferred,(它是job的子类)
他有两个实现类
//任务创建后会立即启动
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,就写到这,下面开始修改自己封装的框架。