携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
前言
上一篇文章中,我们通过对协程一些基本概念的了解,对协程的简单使用有了初步的轮廓,本文将继续介绍Kotlin协程中几个比较常见的知识点,分别是协程中异常传递与取消。
正文
回调:
相信大家对回调这个词都不陌生,甚至天天跟它打交道,以下是使用回调的一个例子:
private fun callback1(id: Int,callback: ICallback<User>){
requireUser(id, object : Callback<User> {
override fun onSuccess(response: Response<User>) {
if (response.body == null){
callback.onFail(NullPointerException("response body is null"))
}else{
callback.onSuccessful(response.body)
}
}
override fun onFail(e: Throwable) {
callback.onFail(e)
}
})
}
调用的时候:
callback1(object : ICallback<User>{
override fun onSuccessful(value: User) {
mTvUserName.text = value.name
}
override fun onFail(e: Throwable) {
Toast.makeText(this@MainActivity,e.message,Toast.LENGTH_SHORT).show()
}
})
以上代码在平常开发中会经常碰到类似的,但是层级多了,就会加大理清回调的逻辑,很容易陷入回调地狱。这样存在的问题也很明显,当我们看写的代码是回调然后一层套一层,可以想象挺那个的。
接下来看看使用的协程是怎么样的:
private suspend fun callback2(): User{
return suspendCancellableCoroutine<User> { coroutine ->
requireUser(0, object : Callback<User> {
override fun onSuccess(response: Response<User>) {
if (response.body == null){
coroutine.resumeWithException(NullPointerException("response body is null"))
}else{
coroutine.resume(response.body)
}
}
override fun onFail(e: Throwable) {
coroutine.resumeWithException(e)
}
})
}
}
通过resume方法来给方法返回结果,或者通过resumeWithException方法来给调用传递异常。
调用的代码:
mMainScope.launch {
try {
val user = callback2()
mTvUserName.text = user.name
}catch (e: Throwable){
Toast.makeText(this@MainActivity,e.message,Toast.LENGTH_SHORT).show()
}
}
调用的代码只需launch一个协程,然后里边执行的代码是串行的,看起来就像是同步的,而且也去除掉了回调,可以切换线程,比如说callback2方法执行的是在IO线程中,那么它返回来后,由于mMainScope的调度器是指定在主线程的,所以回来的操作也是在主线程中。
异常传递:
异常的传递可以为双向传递和单向传递,双向传递是指子协程发生异常的时候,会向外传给父协程,父协程发生异常时则会结束掉子协程。
//子传给父
private suspend fun coroutineScope1(){
Log.i(myTag,"start")
try {
coroutineScope {
Log.i(myTag,"1")
launch {
Log.i(myTag,"2")
throw Exception("test")
}
delay(1000)
Log.i(myTag,"3")
}
}catch (e: Exception){
Log.i(myTag,"4 ${e.message}")
}
Log.i(myTag,"end")
}
输出结果:
12-09 17:36:27.420 18061-18061/com.sendi.coroutinedemo I/MainActivity: start
12-09 17:36:27.430 18061-18061/com.sendi.coroutinedemo I/MainActivity: 1
12-09 17:36:27.590 18061-18061/com.sendi.coroutinedemo I/MainActivity: 2
12-09 17:36:27.780 18061-18061/com.sendi.coroutinedemo I/MainActivity: 4 test
12-09 17:36:27.780 18061-18061/com.sendi.coroutinedemo I/MainActivity: end
//父传给子
private suspend fun coroutineScope2(){
Log.i(myTag,"start")
try {
coroutineScope {
Log.i(myTag,"1")
launch {
try {
delay(1000)
Log.i(myTag,"2")
}catch (e: Exception){
Log.i(myTag,"3 ${e.message}")
}
}
delay(100)
throw Exception("test")
}
}catch (e: Exception){
Log.i(myTag,"4 ${e.message}")
}
Log.i(myTag,"end")
}
输出结果:
12-09 17:41:40.050 20082-20082/com.sendi.coroutinedemo I/MainActivity: start
12-09 17:41:40.050 20082-20082/com.sendi.coroutinedemo I/MainActivity: 1
12-09 17:41:40.260 20082-20082/com.sendi.coroutinedemo I/MainActivity: 3 Parent job is Cancelling
12-09 17:41:40.260 20082-20082/com.sendi.coroutinedemo I/MainActivity: 4 test
12-09 17:41:40.260 20082-20082/com.sendi.coroutinedemo I/MainActivity: end
取消:
取消则是协程在执行的时候,通过调用cancel或者是父协程进行取消,进而让协程停止执行。取消其实就是将状态变成取消的状态,具体还得看启动的协程是否会支持停止掉当前的协程。
private suspend fun cancel1(){
Log.i(myTag,"start")
mMainScope.launch {
Log.i(myTag,"1")
delay(100)
Log.i(myTag,"2")
}
delay(100)
mMainScope.cancel()
Log.i(myTag,"end")
}
输出:
12-09 17:54:06.890 23255-23255/com.sendi.coroutinedemo I/MainActivity: start
12-09 17:54:06.970 23255-23255/com.sendi.coroutinedemo I/MainActivity: 1
12-09 17:54:07.100 23255-23255/com.sendi.coroutinedemo I/MainActivity: end
private suspend fun execute(){
supervisorScope {
val job = launch {
val result = cancel2(1)
Log.i(myTag,"result is $result")
}
delay(50)
Log.i(myTag,"after delay")
job.cancel()
}
}
private suspend fun cancel2(i: Int): String{
return suspendCancellableCoroutine<String> { coroutine->
Log.i(myTag,"1")
coroutine.invokeOnCancellation {
Log.i(myTag,"cancel")
}
Log.i(myTag,"2")
//todo:挂起操作
mMainScope.launch {
Log.i(myTag,"3")
delay(200)
}
}
}
输出结果:
12-09 18:24:42.680 6192-6240/com.sendi.coroutinedemo I/MainActivity: 1
12-09 18:24:42.680 6192-6240/com.sendi.coroutinedemo I/MainActivity: 2
12-09 18:24:42.700 6192-6192/com.sendi.coroutinedemo I/MainActivity: 3
12-09 18:24:42.730 6192-6239/com.sendi.coroutinedemo I/MainActivity: after delay
12-09 18:24:42.730 6192-6239/com.sendi.coroutinedemo I/MainActivity: cancel
结语
本次对协程的相关知识的介绍就到这里了,主要了解协程的取消顺序逻辑、异常传递过程,内容比较短。