1. Review of the core concepts of coroutines

Structured Concurrency

The CoroutineScope/container is handling the container.

Job (Job/SupervisorJob)

Launch coroutine (async)

2. Cancellation of coroutines

2.1 Cancellation of coroutines

  • Cancellation of scope or job

The sample code

suspend fun c01_cancle() { val scope = CoroutineScope(Job()) val job1 = scope.launch { } val job2 = scope.launch { } // Cancel job job1.cancel() job2.cancel() // Cancel scope scope.cancel()}Copy the code

Note: Coroutines cannot be opened again in a canceled scope

2.2 Ensure that coroutines can be cancelled

  • The cancellation of coroutine only marks the cancellation state of coroutine, and does not really cancel coroutine

Sample code:

val job = launch(Dispatchers.Default) { var i = 0 while (i < 5) { println("Hello ${i++}") Thread.sleep(100) } } delay(200) println("Cancel!" Job.cancel () Job.cancel () Hello 0 Hello 1 Hello 2 cancel! Hello 3 Hello 4Copy the code
  • You can use isActive ensureActive() yield to check at critical locations to ensure that the coroutine shuts down properly
Val job = launch(dispatchers.default) {var I = 0 while (I < 5 &&isactive) {var I = 0 while (I < 5 &&isactive) {var I = 0 while (I < 5 &&isactive) println("Hello ${i++}") Thread.sleep(100) } } delay(200) println("Cancel!" ) job.cancel()Copy the code

2.3 Resource shutdown after coroutine cancellation

  • Try /finally can close resources
Launch {try {openIo() throw ArithmeticException()} finally {println(" coroutine end ") closeIo()// close file IO}}Copy the code
  • Note: Suspended functions cannot be called in finally (withContext(NonCancellable) is required if you must call it, not recommended).
Launch {try {work()} finally {//withContext(NonCancellable) WithContext (NonCancellable) {delay(1000L) println("Cleanup done!" )}}}Copy the code

2.4 CancellationException will be ignored

val job = launch { try { delay(Long.MAX_VALUE) } catch (e: Exception) {println (" catch an Exception $e ") / / print: catch exceptions to a Java. Util. Concurrent. CancellationException: }} yield() job.cancel(CancellationException(" I am a CancellationException ")) job.join()Copy the code

3. Abnormal propagation mechanism of coroutines

3.1 Catching coroutine exceptions

3.1.1 try/catch

  • Try /catch business code
Throw ArithmeticException(" ArithmeticException ")} catch (e: Exception) {println (" catch an Exception $e ")}} / / print: catch exceptions to a Java lang. ArithmeticException: calculation errorCopy the code
  • Try/catch coroutines
Try {launch {throw ArithmeticException(" arithmeticerror ")}} catch (e: Exception) {println (" catch an Exception $e ")} / / cannot capture the error logs Exception in the thread "is the main" Java. Lang. ArithmeticException: Calculation error at com. Jinbo. Kotlin. Coroutine. C05_Exception $testDemo $2 $1. InvokeSuspend ats (C05_Exception. Kt: 65) kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56) at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274) at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:84) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59) at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38) at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source) at com.jinbo.kotlin.coroutine.C05_Exception.main(C05_Exception.kt:17)Copy the code
  • Coroutine exceptions cannot be caught by an external try-catch statement

3.1.2 CoroutineExceptionHandler capture anomalies

supervisorScope { val exceptionHandler = CoroutineExceptionHandler { _, E -> println(" catch an exception $e") {launch(exceptionHandler) {throw ArithmeticException(" calculate error ")}} / / catch exceptions to a Java lang. ArithmeticException: calculation errorCopy the code

3.1.3 Catching exceptions

Var catching = kotlin. RunCatching {"hello" throw ArithmeticException(" I am an exception ")} if (catching. IsSuccess) { Println (${catching. GetOrNull ()}")} else {println(${catching. GetOrNull ()}")}Copy the code

At this point, we must introduce the anomaly propagation mechanism of coroutines

3.2 Propagation mechanism of synergistic scope

3.2.1 features

  • Bidirectional propagation, cancelling the child coroutine, cancelling itself, propagating to the parent coroutine

[Synergistic scope propagation characteristics]

CoroutineScope {launch {launch {// The exception of the subcoroutine, which propagates throw ArithmeticException()}} launch {launch {}}}Copy the code

3.2.2 Child coroutines cannot catch their own exceptions, only the parent coroutine can

Val scope = CoroutineScope(Job()) // The parent coroutine (root coroutine) can catch exceptionHandler. Launch (exceptionHandler) {launch {throw ArithmeticException(" I am a child exception ")} Launch (exceptionHandler) {// throw ArithmeticException(" I am the other child exception ") //}}Copy the code

3.2.3 The original exception will be processed by the parent coroutine only after all the children of the parent coroutine have finished

Val handler = CoroutineExceptionHandler {_, exception - > println (" catch exceptions: $exception")} val job = globalscope.launch (handler) {launch {delay(long.max_value)} finally { WithContext (NonCancellable) {println(" The first child coroutine is running, ") delay(100) println(" now the subcoroutine is done ")}}} launch {// second subcoroutine delay(10) println(" second subcoroutine is out ") throw ArithmeticException()}} jot.join () // Print bar: the second child of the coroutine is running, so it won't handle the exception for the time being. Now the subroutine is done catching the exception: java.lang.ArithmeticExceptionCopy the code

3.2.4 Abnormal Aggregation

The first exception that occurs will be processed prior to Y, and all other exceptions that occur after this point will be added to the first exception as suppressed

Val handler = CoroutineExceptionHandler {_, exception - > println (" catch exceptions: $exception ${exception.suppressed.contentToString()}") } val job = GlobalScope.launch(handler) { launch { delay(100) Launch {try {delay(long.max_value); // When another coroutine of the same class fails due to IOException, It will be cancelled} finally {throw ArithmeticException() // while throwing the second exception}} delay(long.max_value)} job.join() java.io.IOException [java.lang.ArithmeticException]Copy the code

3.2.5 Launch and Async exception handling

  • Launch throws an exception directly without waiting
Launch {throw ArithmeticException (abnormal "launch")} / / print the Exception in the thread "is the main" Java. Lang. ArithmeticException: launch an ExceptionCopy the code
  • Async expects to report back an exception when the user calls await()

Async will throw an exception in await() directly when the root coroutine (GlobalScope) or supervisor child coroutine is used

SupervisorScope {val deferred = async {throw ArithmeticException(" exception ")}Copy the code
  • Throw an exception only when await()
SupervisorScope {val deferred = async {throw ArithmeticException(" exception ")} try {deferred. Await ()} catch (e: Exception) {println (" catch an Exception $e ")}} / / print results: catch exceptions to a Java lang. ArithmeticException: ExceptionCopy the code
  • Tips: If not directly inRoot coroutine (GlobalScope)supervisorSubcoroutine,Async and launch behave the sameThrow an exception directly, and do not throw an exception while await()
SupervisorScope {launch {val deferred = async {throw ArithmeticException(" exception ")}}Copy the code

3.2.6 coroutineScope external Try-catch (Supervisor does not)

Try {coroutineScope {launch {throw ArithmeticException(" exception ")}}} catch (e: Exception) {println (" to catch exceptions: $e ")} / / print results: capture the Exception: Java. Lang. ArithmeticException: ExceptionCopy the code

3.3 Monitor the propagation mechanism of the scope

3.3.1 Features are propagated one-way downwards

  • Communication mechanisms for oversight scopes (right to independent decision making?)

- Supervisor example codeCopy the code

3.3.2 rainfall distribution on 10-12 coroutines can CoroutineExceptionHandler alone setting

Issupervisorscope {launch(exceptionHandler) {throw ArithmeticException(" exception is present ")} Found abnormal Java. Lang. ArithmeticException: anomaly appearedCopy the code

3.3.3 Supervision is only useful for its immediate subroutines

It is useful only for its direct child coroutines launch(exceptionHandler) {throw ArithmeticException(" exception is present ")}}Copy the code
The subroutine overseeing the operation is unable to handle exceptions independently, Launch (exceptionHandler) {throw ArithmeticException(" exception is present ")}}}  Exception in thread "main" java.lang.ArithmeticException: Anomaly appeared at com. Jinbo. Kotlin. The coroutine. C05_Exception $testDemo $2 $1 $1 $1. InvokeSuspend ats (C05_Exception. Kt: 1039) kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56) at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274) at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:84) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59) at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38) at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source) at com.jinbo.kotlin.coroutine.C05_Exception.main(C05_Exception.kt:17)Copy the code

3.4 use coroutineExceptionHandler correctly

3.4.1 Root Coroutine (GlobalScope)//TODO validation

  GlobalScope.launch(exceptionHandler) {  }
Copy the code

It is designed for direct subdivision

Container {launch(exceptionHandler) {throw ArithmeticException(" exception is present ")}}Copy the code

3.4.3 Hand-created Scope(Job()/SupervisorJob())

Val scope = CoroutineScope(Job()) scope.launch(exceptionHandler) {throw ArithmeticException(" exception ")}Copy the code

4 think

4.1 Android synergy

  • viewmodelScope lifecycleScope

The resources

How does one of the coroutines (Part 1) require an exception Cancellation Coroutines – Part 3 Coroutines & Patterns for work that shouldn’t be cancelled be cancelled-Part 4

Copy the code