Kotlin 协程
一,异步操作(仿RxJava)
Android APP中,使用RxJava在很多时候都很方便,但是从apk包解析来看,RxJava的包有点大。。。 所以自从用了Kotlin之后,我已经想着怎么把Rxjava从项目中删除掉。 一般我使用RxJava更多的是用来进行异步处理,比如说是在非UI线程中处理耗时操作,然后在UI线程中处理结果,使用Kotlin的协程也可以达到类似效果。
fun <R> processOnAsync(action: () -> R,
success: (R) -> Unit,
fail: (Throwable) -> Unit): Job {
return launch {
val result = suspendCancellableCoroutine<R> { continuation ->
continuation.invokeOnCompletion {
if (continuation.isCancelled) {
NonCancellable.cancel()
}
}
if (continuation.isActive) {
try {
continuation.resume(action())
} catch (exp: Throwable) {
continuation.resumeWithException(exp)
}
}
}
launch (UI) {//将结果返回到UI线程中
when (result) {
is Throwable -> {
fail(result)
}
else -> {
success(result)
}
}
}
}
}
复制代码
使用方法:
processOnAsync({
//耗时操作,注意这里返回的是最后一行,或者使用return来指定返回
}, success = {
//成功时的处理代码
}, fail = {
//失败时的处理代码
})
复制代码
(仿异步操作还有别的方法,比如组合async和launch来实现也可以)
二,另一种异步操作
如果需要实现的功能是:在一个异步操作中一直等待结果,再把这个结果返回。那么(一)就无法满足了,只能使用如下方法:
fun <R> processWithAsync(block: (CancellableContinuation<R>) -> Unit,
success: (R) -> Unit,
fail: (Throwable) -> Unit): Job {
return launch {
val result = suspendCancellableCoroutine<R> { continuation ->
continuation.invokeOnCompletion {
if (continuation.isCancelled) {
NonCancellable.cancel()
}
}
if (continuation.isActive) {
try {
block(continuation)
} catch (exp: Throwable) {
continuation.resumeWithException(exp)
}
}
}
launch (UI) {
when (result) {
is Throwable -> {
fail(result)
}
else -> {
success(result)
}
}
}
}
}
复制代码
使用方法也类似:
processOnAsync<String>({ cont ->
//耗时操作,注意这里并不会主动返回,
//直到你代码调用cont.resumt(string) 或者 cont.resumeWithException(error throwable)
}, success = {
//成功时的处理代码
}, fail = {
//失败时的处理代码
})
复制代码
三,仿concatMap
有了前面(二)的基础,也容易仿制一个简单的concatMap了。
fun <T, R> processAndConcatMap(list: List<T>,
block: (CancellableContinuation<R>, Int, T) -> Unit,
stepCtx: CoroutineContext = UI, step: (Int, R) -> Unit,
failCtx: CoroutineContext = UI, fail: (Int, Throwable) -> Unit,
doneCtx: CoroutineContext = UI, done: (List<T>) -> Unit = {}): Job {
list.isNotEmpty() || return Job()
return launch {
var errorThrow = false
list.forEachIndexed { index, value ->
if (errorThrow) {
return@forEachIndexed
}
val result = suspendCancellableCoroutine<R> { continuation ->
continuation.invokeOnCompletion {
if (continuation.isCancelled) {
NonCancellable.cancel()
}
}
if (continuation.isActive) {
try {
block(continuation, index, value)
} catch (exp: Throwable) {
continuation.resumeWithException(exp)
}
}
}
when (result) {
is Throwable -> {
launch (failCtx) {
fail(index, result)
}
}
else -> {
launch (stepCtx) {
step(index, result)
}
}
}
if (result is Throwable) {
errorThrow = true
return@forEachIndexed
}
}
if (!errorThrow) {
launch (doneCtx) {
done(list)
}
}
}
}
复制代码
注意入参中的stepCtx: CoroutineContext = UI这类参数。 这是用于协程切换的,可以控制step,fail,done这三个代码块分别运行于哪个线程中。
使用的时候呢,这样使用:
/*
dataList是List<SourceType>类型,需要在action中做一些耗时操作,
然后把SourceType转化为目标类型Pair<String, String>
*/
processWithConcatMap<SourceType, Pair<String, String>> (
dataList,
block = { cont, _, it ->
//在这里进行SourceType转为Pair<String, String>类型的耗时操作
if (/*normal*/true) {//如果转换成功就调用这里
cont.resume(Pair("Left", "Right"))
} else {//如果转换过程出错则调用这里
cont.resumeWithException(Throwable())
}
},
step = { _, _ ->
},
fail = { _, _ ->
},
done = {
}
)
复制代码
注意这些方法,都设置了Job类型的返回值,这个Job返回值是为了方便取消协程用的