Kotlin 个人小工具集

364 阅读3分钟

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返回值是为了方便取消协程用的