Kotlin 协程协程取消 ① ( 协程作用域取消 | 协程作用域子协程取消 | 通过抛出异常取消协程 | Job#cancel 函数 | 自定义异常取消协程 )

Posted 韩曙亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin 协程协程取消 ① ( 协程作用域取消 | 协程作用域子协程取消 | 通过抛出异常取消协程 | Job#cancel 函数 | 自定义异常取消协程 )相关的知识,希望对你有一定的参考价值。

文章目录





一、协程取消



协程取消 :

  • 取消协程作用域 : 取消 协程作用域 会将该作用域中的 所有 子协程 一同取消 ;
  • 取消子协程 : 子协程 的取消 不会影响 同一层级的 兄弟协程的执行 ;
  • 通过抛出异常取消协程 : 协程取消通常会通过 抛出 CancellationException 异常 实现 ;
  • 挂起函数取消 : 定义在 kotlinx.coroutines 包下的 suspend 挂起函数 是可以取消的 , 如 delay 函数 ;




二、协程作用域取消



创建 协程作用域 CoroutineScope 实例对象 , 传入 调度器 :

// 创建协程作用域
val coroutineScope = CoroutineScope(Dispatchers.Default)

调用 协程作用域的 CoroutineScope#launch 方法 , 可以创建一个子协程 ;

val job0 = coroutineScope.launch 
    Log.i(TAG, "job0 子协程执行开始")
    delay(2000)
    Log.i(TAG, "job0 子协程执行完毕")


完整代码示例 :

  • 首先 , 创建协程作用域 ;
  • 然后 , 在协程作用域中 创建两个子协程 ;
  • 最后 , 取消协程作用域 , 同时该作用域内的两个子协程也一并被取消了 ;
package kim.hsl.coroutine

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*

class MainActivity : AppCompatActivity()
    val TAG = "MainActivity"

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        runBlocking 
            // 创建协程作用域
            val coroutineScope = CoroutineScope(Dispatchers.Default)

            val job0 = coroutineScope.launch 
                Log.i(TAG, "job0 子协程执行开始")
                delay(2000)
                Log.i(TAG, "job0 子协程执行完毕")
            

            val job1 = coroutineScope.launch 
                Log.i(TAG, "job1 子协程执行开始")
                delay(2000)
                Log.i(TAG, "job1 子协程执行完毕")
            

            // 100ms 后取消协程作用域
            delay(100)
            // 取消协程作用域
            coroutineScope.cancel()
        
    

执行结果 : 取消 coroutineScope 协程作用域之后 , 该作用域下的 job0 和 job1 子协程都被取消了 , 两个子协程都没有执行完毕 ;

10:33:33.468  I  job0 子协程执行开始
10:33:33.471  I  job1 子协程执行开始

如果不取消协程作用域 , 应该打印如下内容 :

10:31:49.880  I  job0 子协程执行开始
10:31:49.886  I  job1 子协程执行开始
10:31:51.937  I  job1 子协程执行完毕
10:31:51.938  I  job0 子协程执行完毕




三、协程作用域子协程取消



单独取消 协程作用域 中的 子协程 , 协程作用域 中的其它 兄弟协程不受影响 ;

package kim.hsl.coroutine

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*

class MainActivity : AppCompatActivity()
    val TAG = "MainActivity"

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        runBlocking 
            // 创建协程作用域
            val coroutineScope = CoroutineScope(Dispatchers.Default)

            val job0 = coroutineScope.launch 
                Log.i(TAG, "job0 子协程执行开始")
                delay(2000)
                Log.i(TAG, "job0 子协程执行完毕")
            

            val job1 = coroutineScope.launch 
                Log.i(TAG, "job1 子协程执行开始")
                delay(2000)
                Log.i(TAG, "job1 子协程执行完毕")
            

            // 100ms 后取消协程作用域
            delay(100)
            // 取消协程作用域中的子协程
            job1.cancel()
        
    

执行结果 : 在 协程作用域 coroutineScope 中 启动了 job0 和 job1 两个协程 , 取消了 job1 协程 , job1 协程没有执行完毕 , job0 协程执行完毕 ;





四、通过抛出异常取消协程



1、Job#cancel 函数


调用 Job#cancel 函数 , 取消协程操作 , 该函数原型如下 :

/**
 * 使用可选的取消[原因]取消此作业。
 * 原因可用于指定错误消息或提供关于的其他详细信息
 * 为调试目的而取消的原因。
 * 有关取消机制的完整解释,请参阅[Job]文档。
 */
public fun cancel(cause: CancellationException? = null)

取消协程时 , 可以传入一个 CancellationException 异常实例对象 , 也可以不传 , 默认为 null ;

// 取消协程作用域中的子协程
job1.cancel()

也可以传入一个 自定义 CancellationException 类型的异常 , 取消协程 ;

// 取消协程作用域中的子协程
job1.cancel(CancellationException("自定义 CancellationException 异常"))

由于报出的 CancellationException 异常是正常情况 , 如果需要查看该异常 , 需要在协程中使用 try catch 代码块捕获该异常 ;

val job1 = coroutineScope.launch 
    try 
        Log.i(TAG, "job1 子协程执行开始")
        delay(2000)
        Log.i(TAG, "job1 子协程执行完毕")
    catch (e: Exception) 
        Log.i(TAG, "job1 子协程执行捕获到异常 :")
        e.printStackTrace()
    


2、默认异常取消协程


完整代码示例 :

package kim.hsl.coroutine

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*

class MainActivity : AppCompatActivity()
    val TAG = "MainActivity"

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        runBlocking 
            // 创建协程作用域
            val coroutineScope = CoroutineScope(Dispatchers.Default)

            val job1 = coroutineScope.launch 
                try 
                    Log.i(TAG, "job1 子协程执行开始")
                    delay(2000)
                    Log.i(TAG, "job1 子协程执行完毕")
                catch (e: Exception) 
                    Log.i(TAG, "job1 子协程执行捕获到异常 :")
                    e.printStackTrace()
                
            

            // 100ms 后取消协程作用域
            delay(100)
            // 取消协程作用域中的子协程
            job1.cancel()
        
    

执行结果 :

16:43:17.637  I  job1 子协程执行开始
16:43:17.787  I  job1 子协程执行捕获到异常 :
16:43:17.790  W  kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutineCancelling@bc6a601


3、自定义异常取消协程


传入自定义异常代码示例 :

package kim.hsl.coroutine

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*

class MainActivity : AppCompatActivity()
    val TAG = "MainActivity"

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        runBlocking 
            // 创建协程作用域
            val coroutineScope = CoroutineScope(Dispatchers.Default)

            val job1 = coroutineScope.launch 
                try 
                    Log.i(TAG, "job1 子协程执行开始")
                    delay(2000)
                    Log.i(TAG, "job1 子协程执行完毕")
                catch (e: Exception) 
                    Log.i(TAG, "job1 子协程执行捕获到异常 :")
                    e.printStackTrace()
                
            

            // 100ms 后取消协程作用域
            delay(100)
            // 取消协程作用域中的子协程
            job1.cancel(CancellationException("自定义 CancellationException 异常"))
        
    

执行结果 :

17:20:56.487  I  job1 子协程执行开始
17:20:56.629  I  job1 子协程执行捕获到异常 :
17:20:56.630  W  java.util.concurrent.CancellationException: 自定义 CancellationException 异常
17:20:56.631  W  	at kim.hsl.coroutine.MainActivity$onCreate$1.invokeSuspend(MainActivity.kt:33)
17:20:56.631  W  	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
17:20:56.631  W  	at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:235)
17:20:56.631  W  	at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:167)
17:20:56.631  W  	at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:408)
17:20:56.631  W  	at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:442)
17:20:56.632  W  	at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:431)
17:20:56.632  W  	at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:529)
17:20:56.632  W  	at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.common.kt:497)
17:20:56.632  W  	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:277)
17:20:56.632  W  	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:87)
17:20:56.632  W  	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:61)
17:20:56.633  W  	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source:1)
17:20:56.633  W  	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:40)
17:20:56.633  W  	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source:1)
17:20:56.633  W  	at kim.hsl.coroutine.MainActivity.onCreate(MainActivity.kt:15)
17:20:56.633  W  	at android.app.Activity.performCreate(Activity.java:7144)
17:20:56.633  W  	at android.app.Activity.performCreate(Activity.java:7135)
17:20:56.634  W  	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
17:20:56.634  W  	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2931)
17:20:56.634  W  	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3086)
17:20:56.634  W  	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
17:20:56.634  W  	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
17:20:56.634  W  	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
17:20:56.635  W  	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816)
17:20:56.635  W  	at android.os.Handler.dispatchMessage(Handler.java:106)
17:20:56.635  W  	at android.os.Looper.loop(Looper.java:193)
17:20:56.635  W  	at android.app.ActivityThread.main(ActivityThread.java:6718)
17:20:56.642  W  	at java.lang.reflect.Method.invoke(Native Method)
17:20:56.643  W  	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
17:20:56.643  W  	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
17:20:56.676  D  Skia GL Pipeline

以上是关于Kotlin 协程协程取消 ① ( 协程作用域取消 | 协程作用域子协程取消 | 通过抛出异常取消协程 | Job#cancel 函数 | 自定义异常取消协程 )的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin 协程协程底层实现 ③ ( 结构化并发 | MainScope 作用域 | 取消协程作用域 | Activity 实现 CoroutineScope 协程作用域接口 )

Kotlin 协程协程底层实现 ③ ( 结构化并发 | MainScope 作用域 | 取消协程作用域 | Activity 实现 CoroutineScope 协程作用域接口 )

Kotlin 协程协程异常处理 ② ( SupervisorJob 协程 | supervisorScope 协程作用域构建器函数 )

Kotlin 协程协程异常处理 ② ( SupervisorJob 协程 | supervisorScope 协程作用域构建器函数 )

深入理解Kotlin协程协程作用域启动模式调度器异常和取消使用篇

Kotlin 协程协程底层实现 ④ ( 结构化并发 | viewModelScope 作用域示例 )