前言
RxJava执行异步任务执行结果通常以回调的方式返回,当存在多个异步任务时,返回的多个结果难以协作。而使用Courtines则可以将异步执行的结果返回到同一个代码块里面,这将大大提高了多个异步执行任务的协作能力。
suspendCoroutine
- 它只能在挂起函数或Coroutines作用域中使用。
- 它接收一个带Coroutines上下文参数的Lambda表达式。可用这个Coroutines上下文调用resume()和resumeWithException(e) 恢复挂起的函数。
1
2
3
4
5
6
7
8
9
10
11suspend fun test(): Int{
val i = 2
return suspendCoroutine {
if(i<5){
it.resume(i)
}else{
val e = Exception()
it.resumeWithException(e)
}
}
}
异步执行结果封装
以Room的数据查询为例。先看一个Dao层函数:
1 |
|
它的作用是查询数据库一个字段的信息,以Diary对象返回数据。但总所周知Room操作数据库是不允许直接在UI线程调用的。解决办法:
- 我们可以手动 new 一个Thread执行,然后再切回主线程更新UI;
- 更常规操作是直接用RxJava来切换线程,处理该逻辑。
然而现在用Coroutines可以用很少的代码就可以封装一个函数完成这个需求。
1 | private suspend fun <T> execute(block: () -> T): T{ |
execute() 接受一个lambda表达式,我们可以把异步的逻辑传入,withContext(){} 使用调度器使得逻辑在IO线程执行,执行结果在被调用的线程返回,我们在主线程调用那么就是在主线程返回数据。
用封装好的 execute() 调用刚才那个Dao层的 loadDiaries() 函数查询id为1和id为2 生成一个list返回即可。
1 | suspend fun loadDiaries() = execute{ |
像这样将两个异步任务结果合并在一起采用回调的方式很难做到。
出现异常时Coroutines会将其抛出,可以使用try-catch捕获。
1 | scope.launch { |
带回调的结果封装
很多时候对于异步操作,所用的库都会用回调给你封装好了。通常类似于这样的结构
1 | Call<A>.execute(object: Listener<A>(){ |
想要摆脱回调带来的苦恼,可以用Coroutines来进一步封装。
1 | suspend fun <T>Call<T>.await(): T{ |
写一个Call的 拓展函数, 使用 suspendCoroutine{} 将请求挂起,当接受到结果时恢复。
封装好后以后无论进行多少次请求,都只写一此回调函数就行了。
1 | /** |
如上, 进行多次请求都直接调用 .await() 就行。