kotlin coroutine源码解析之Job取消以及异常的处理流程分析

5 篇文章 0 订阅
5 篇文章 0 订阅

Job取消cancel过程分析

Job取消cancel过程非常复杂,尤其是多个父子Job嵌套,以及内部又拥有挂起函数的情况下,流程是相互依赖,相互等待的过程,所有这里只做最简单的Job情况,方便代码跟踪。

还是从一段最简单的代码开始跟踪,代码如下:

fun testJobTree() {

    val myScope = CoroutineScope(CoroutineName("name") + IO + Job() + CoroutineExceptionHandler{ _, t -> })

    val job = myScope.launch {
		println("job has run to end")
    }

	println("job has launch")
    job.cancel("cancel job0")
}

job的工作是打印一句话这个job的工作就完成了。在Job启动后,立刻调用cancel方法取消该Job。

我们从cancel方法跟踪进去,得到的流程如下:
在这里插入图片描述

当我们调用cancel方法之后,会依次进入:
cancel(cause: CancellationException?) -> cancelInternal(cause: Throwable) -> cancelImp(cause: Any?)
从cancelImp开始具体分析 (cancelImp这个方法比较重要,很多地方会调用,子Job通知父Job取消的方法,也会有可能让父Job去调用这个取消方法,parentHandler.childCancelled() -> parent.cancelImp())。

makeCancelling()代码如下:

   internal fun cancelImpl(cause: Any?): Boolean {
        var finalState: Any? = COMPLETING_ALREADY
        if (onCancelComplete) {
            // make sure it is completing, if cancelMakeCompleting returns state it means it had make it
            // completing and had recorded exception
            finalState = cancelMakeCompleting(cause)
            if (finalState === COMPLETING_WAITING_CHILDREN) return true
        }
        if (finalState === COMPLETING_ALREADY) {
            finalState = makeCancelling(cause)
        }
        return when {
            finalState === COMPLETING_ALREADY -> true
            finalState === COMPLETING_WAITING_CHILDREN -> true
            finalState === TOO_LATE_TO_CANCEL -> false
            else -> {
                afterCompletion(finalState)
                true
            }
        }
    }

我们的代码不会出现上面判断的一个if (onCancelComplete)条件成立的情况,onCancelComplete一般都是false,这个判断直接跳过,会来到makeCancelling方法:

cancel Job的过程中,job会存在以下四种状态:

// COMPLETING_ALREADY -- when already complete or completing
说明已经完成或者是正在完成

// COMPLETING_RETRY -- when need to retry due to interference
说明任务取消需要重试

 // COMPLETING_WAITING_CHILDREN -- when made completing and is waiting for children
 等待孩子Job结束,自己才能结束

// final state -- when completed, for call to afterCompletion
当结束后,等待调用afterCompletion回调通知

其中,如果Job处于Completing_Already或者是Completing_Waiting_children状态,多次取消cancel操作,将会被忽略,不会继续进行下面的取消的进一步操作。

makeCancelling方法会有三个分叉:
1.state是否是Finishing的状态,这种状态下直接调用:notifyCancelling(state.list, it)
2.state是InComplete的状态,然后还要判断是否是isActive的状态,
是活跃状态下,调用:tryMakeCancelling(state, causeException)
非活跃状态下,调用:tryMakeCompleting(state, CompletedExceptionally(causeException))

那么我们先分析第一种最简单的情况,notifyCancelling()方法,这个方法源码如下:

	//JobSupport类
    private fun notifyCancelling(list: NodeList, cause: Throwable) {
        // first cancel our own children
        onCancelling(cause)
        notifyHandlers<JobCancellingNode>(list, cause)
        // then cancel parent
        cancelParent(cause) // tentative cancellation -- does not matter if there is no parent
    }

第一个方法不重要,是需要子类去覆写的,是个空函数,
第二个方法是通知state的数据结构每个节点调用它的invoke方法,如果是ChildHandleNode节点的话,那么代理调用的是child.parentCancelled()方法,如果是其他节点的可能其他效果(这个节点类型后面再详解,有很多种);那么综上来看,这个方法就是父Job通知子Job,父Job已经取消了,子Job需要做对应的处理了、
第三个方法是Job需要通知并取消父Job、


    //JobSupport类
    private inline fun <reified T: JobNode> notifyHandlers(list: NodeList, cause: Throwable?) {
        var exception: Throwable? = null
        list.forEach<T> { node ->
            try {
                node.invoke(cause)
            } catch (ex: Throwable) {
                exception?.apply { addSuppressedThrowable(ex) } ?: run {
                    exception =  CompletionHandlerException("Exception in completion handler $node for $this", ex)
                }
            }
        }
        exception?.let { handleOnCompletionException(it) }
    }
		

可以看到上面的方法里面,是遍历state.list的数组,调用每个节点的invoke方法,其中node如果是ChildHandlerNode类型的话,是对应的子Job节点,看下这个类的结构:

internal class ChildHandleNode(
    @JvmField val childJob: ChildJob
) : JobCancellingNode(), ChildHandle {
    override val parent: Job get() = job
    override fun invoke(cause: Throwable?) = childJob.parentCancelled(job)
    override fun childCancelled(cause: Throwable): Boolean = job.childCancelled(cause)
}

触发的是invoke(cause: Throwable?) = childJob.parentCancelled(job),也就是通知子Job去取消。

	//JobSupport类
	private fun cancelParent(cause: Throwable): Boolean {
        // Is scoped coroutine -- don't propagate, will be rethrown
        if (isScopedCoroutine) return true

        /* CancellationException is considered "normal" and parent usually is not cancelled when child produces it.
         * This allow parent to cancel its children (normally) without being cancelled itself, unless
         * child crashes and produce some other exception during its completion.
         */
        val isCancellation = cause is CancellationException
        val parent = parentHandle
        // No parent -- ignore CE, report other exceptions.
        if (parent === null || parent === NonDisposableHandle) {
            return isCancellation
        }

        // Notify parent but don't forget to check cancellation
        return parent.childCancelled(cause) || isCancellation
    }

cancelParent()方法是取消父Job的作用,首先判断这个错误是不是取消异常(除了取消异常,还要其他抛出的Jvm异常等等),如果parent是空的,或者parent已经没有关联了,那么返回isCancellation的bol值,如果不是,那么调用parent.childCancelled()方法,返回 结果与isCancellation的bool值。

我们来看下childCancelled()方法:

job的类型有两种,一个是JobSupport也就是最常用的Job,还有一种是supervisorJob,这个覆写了childCancelled方法。

//JobSupport
public open fun childCancelled(cause: Throwable): Boolean {
    if (cause is CancellationException) return true
    return cancelImpl(cause) && handlesException
}

上面JobSupport内,childCancelled方法,对于来自子Job的取消异常CancellationException是不会让父job取消的,直接返回true,说明消耗了事件但是不取消自己;而Job的其他异常是会调用父Job自己的cancelImp方法,也就是会让父Job取消。

private class SupervisorJobImpl(parent: Job?) : JobImpl(parent) {
    override fun childCancelled(cause: Throwable): Boolean = false
}

上面的supervisorJob是覆写了这个方法,直接返回false,也就是说,来自子Job的取消异常和其他异常都不会让父Job取消自己的,返回false代表父Job没有处理,不消耗这个通知事件,需要子Job自己去处理。

通过这个方法可以看出来,父Job无论是job还是supervisorJob都是不处理cancelledException的,其他的异常的话,普通父Job会被取消,supervisorJob会无视这个异常不处理,抛回给子Job自己处理。

我们来做个代码演示:

val myScope = CoroutineScope(CoroutineName("name") + IO + Job() + CoroutineExceptionHandler{ c, e -> println(e.message)})

val job = myScope.launch {

    var job2 : Job? = launch {
        println("job2 begin")
        delay(1000)
        println("job2 end")
    }

    var job3 : Job? = launch {
        println("job3 begin")
        delay(2000)
        println("job3 end")
    }

    job2?.cancel("cancel job")
    println("job has run to end")
}

println("job has launch")

按照刚刚分析的代码来看,job3的打印输出是不被job2的取消打断的,打印结果如下:

2022-11-15 15:46:17.608  E/MainActivity: main : job has launch
2022-11-15 15:46:17.611  E/MainActivity: DefaultDispatcher-worker-2 : job2 begin
2022-11-15 15:46:17.613  E/MainActivity: DefaultDispatcher-worker-1 : job has run to end
2022-11-15 15:46:17.614  E/MainActivity: DefaultDispatcher-worker-1 : job3 begin
2022-11-15 15:46:19.619  E/MainActivity: DefaultDispatcher-worker-2 : job3 end

确实如此,除了Job2自己被取消了,其他Job都没有受到影响。job2是要delay去等待是为了协程可以去检测当前状态是否取消,sleep是线程级别的,协程是没有办法在这里去检查的。

在看一个抛出异常的例子:

val myScope = CoroutineScope(CoroutineName("name") + IO + Job() + CoroutineExceptionHandler{ c, e ->
            println(c[CoroutineName].toString() + e.message)
        })


        val job = myScope.launch {

            var job2 : Job? = launch {
                println("job2 begin")
                delay(100)
                throw Exception("测试异常报错")
                println("job2 end")
            }

            var job3 : Job? = launch {
                println("job3 begin")
                delay(2000)
                println("job3 end")
            }

            println("job has run to end")
        }

        println("job has launch")

打印的结果是:

2022-11-15 15:49:09.338  E/MainActivity: main : job has launch
2022-11-15 15:49:09.342  E/MainActivity: DefaultDispatcher-worker-1 : job has run to end
2022-11-15 15:49:09.343  E/MainActivity: DefaultDispatcher-worker-1 : job2 begin
2022-11-15 15:49:09.345  E/MainActivity: DefaultDispatcher-worker-4 : job3 begin
2022-11-15 15:49:09.448  E/MainActivity: DefaultDispatcher-worker-7 : CoroutineName(name)测试异常报错

可以看出来,job2,job3都启动成功了,但是随着job2的抛出异常,job2,job3的最后一条语句都没有打印,说明异常抛出的话,如果父Job是普通的Job,那么父Job会取消,父Job也会通知它的所有子Job取消。

再来看一个supervisorJob的例子:

val myScope = CoroutineScope(CoroutineName("name") + IO + Job() + CoroutineExceptionHandler{ c, e ->
   println("CoroutineScope exceptionHandler: " + c[CoroutineName].toString() + e.message)
})

val job = myScope.launch {

   var job2 : Job? = launch(SupervisorJob(coroutineContext[Job])) {
       println("job2 begin")
       delay(100)
       throw Exception("测试异常报错")
       println("job2 end")
   }

   var job3 : Job? = launch {
       println("job3 begin")
       delay(2000)
       println("job3 end")
   }


   println("job has run to end")
}

println("job has launch")

打印的结果如下:

coroutineTestActivity: job has launch
job2 begin
coroutineTestActivity: job has run to end
job3 begin
coroutineTestActivity: CoroutineScope exceptionHandler: CoroutineName(name)测试异常报错
job3 end

可以看到Job2虽然抛出了异常,但是Job3还是完整的跑完了,没有中断,说明supervisorJob起到作用了,上面分析了,SupervisorJob遇到子Job的childCancelled方法回调是不处理的,所以只会在内部自己处理异常。

我们是在launch方法中传入了supervisorJob参数:

   var job2 : Job? = launch(SupervisorJob(coroutineContext[Job])) {
       println("job2 begin")
       delay(100)
       throw Exception("测试异常报错")
       println("job2 end")
   }

看下SupervisorJob(coroutineContext[Job])这个构造函数做了什么:

public fun SupervisorJob(parent: Job? = null) : CompletableJob = SupervisorJobImpl(parent)

private class SupervisorJobImpl(parent: Job?) : JobImpl(parent) {
    override fun childCancelled(cause: Throwable): Boolean = false
}

internal open class JobImpl(parent: Job?) : JobSupport(true), CompletableJob {
    init { initParentJob(parent) } //这个函数上一章分析过,是关联父子Job的
	//省略、、、
}

SupervisorJob -> SupervisorJobImpl -> JobImpl.initParentJob(parent)
我们直接看initParentJob(parent) 这个方法,这个方法我们在上一章 解析launch启动过程已经分析过了,这个方法是让parent和本身Job进行父子关联。然后supervisorJob作为参数传入launch方法,那么supervisorJob又和Job2产生了父子关系。这样我们就知道了例子中的代码的Job树的结构是怎么样的了:
在这里插入图片描述
那么Job2如果抛出异常的话,这个异常会被supervisorJob忽略掉,其他Job不会收到影响。

Job的异常处理

job的异常处理是和上面一节内容相关的,我们已经分析过了,SupervisorJob不会处理来自子Job的异常情况,那么这个来自子Job的异常情况该由谁来处理呢?我们从fun finalizeFinishingState(state: Finishing, proposedUpdate: Any?): Any?这个方法开始分析:

// Finalizes Finishing -> Completed (terminal state) transition.
// ## IMPORTANT INVARIANT: Only one thread can be concurrently invoking this method.
// Returns final state that was created and updated to
private fun finalizeFinishingState(state: Finishing, proposedUpdate: Any?): Any? {
        /*
         * Note: proposed state can be Incomplete, e.g.
         * async {
         *     something.invokeOnCompletion {} // <- returns handle which implements Incomplete under the hood
         * }
         */
        //----------------------检查当前state的状态----------------------------
        assert { this.state === state } // consistency check -- it cannot change
        assert { !state.isSealed } // consistency check -- cannot be sealed yet
        assert { state.isCompleting } // consistency check -- must be marked as completing
        val proposedException = (proposedUpdate as? CompletedExceptionally)?.cause
        // Create the final exception and seal the state so that no more exceptions can be added

        //----------------------收集错误信息----------------------------------
        var wasCancelling = false // KLUDGE: we cannot have contract for our own expect fun synchronized
        val finalException = synchronized(state) {
            wasCancelling = state.isCancelling
            val exceptions = state.sealLocked(proposedException)
            val finalCause = getFinalRootCause(state, exceptions)
            if (finalCause != null) addSuppressedExceptions(finalCause, exceptions)
            finalCause
        }
        // Create the final state object
        val finalState = when {
            // was not cancelled (no exception) -> use proposed update value
            finalException == null -> proposedUpdate
            // small optimization when we can used proposeUpdate object as is on cancellation
            finalException === proposedException -> proposedUpdate
            // cancelled job final state
            else -> CompletedExceptionally(finalException)
        }
        
        //----------------------异常处理----------------------------------
		// Now handle the final exception
        if (finalException != null) {
            val handled = cancelParent(finalException) || handleJobException(finalException)
            if (handled) (finalState as CompletedExceptionally).makeHandled()
        }


        // Process state updates for the final state before the state of the Job is actually set to the final state
        // to avoid races where outside observer may see the job in the final state, yet exception is not handled yet.
        if (!wasCancelling) onCancelling(finalException)
        onCompletionInternal(finalState)
        // Then CAS to completed state -> it must succeed
        val casSuccess = _state.compareAndSet(state, finalState.boxIncomplete())
        assert { casSuccess }
        // And process all post-completion actions
        completeStateFinalization(state, finalState)
        return finalState
    }

我们直接看异常处理部分代码,其他部分代码可以暂时不用关心,先调用了cancelParent(finalException) 方法,根据上面的分析画出流程图:
在这里插入图片描述
最后异常会由Job自己进行处理,调用了handleJobException方法进行处理:

private open class StandaloneCoroutine(
    parentContext: CoroutineContext,
    active: Boolean
) : AbstractCoroutine<Unit>(parentContext, initParentJob = true, active = active) {
    override fun handleJobException(exception: Throwable): Boolean {
        handleCoroutineException(context, exception)
        return true
    }
}

@InternalCoroutinesApi
public fun handleCoroutineException(context: CoroutineContext, exception: Throwable) {
    // Invoke an exception handler from the context if present
    try {
        context[CoroutineExceptionHandler]?.let {
            it.handleException(context, exception)
            return
        }
    } catch (t: Throwable) {
        handleCoroutineExceptionImpl(context, handlerException(exception, t))
        return
    }
    // If a handler is not present in the context or an exception was thrown, fallback to the global handler
    handleCoroutineExceptionImpl(context, exception)
}

可以看到调用的是被StandaloneCoroutine覆写的handleJobException方法 -> handleCoroutineException方法,里面处理方式是:先取出这个协程的上下文集合中的ExceptionHandle来对异常进行处理,这个context定义如下:

    public final override val context: CoroutineContext = parentContext + this

launch的时候,继承自scopeCoroutine的上下文,然后用自己Job类型去覆盖集合中的元素。如果这个context[CoroutineExceptionHandler]不存在的话,那么下面还需要调用handleCoroutineExceptionImpl方法,代码如下:

internal actual fun handleCoroutineExceptionImpl(context: CoroutineContext, exception: Throwable) {
    // use additional extension handlers
    for (handler in handlers) {
        try {
            handler.handleException(context, exception)
        } catch (t: Throwable) {
            // Use thread's handler if custom handler failed to handle exception
            val currentThread = Thread.currentThread()
            currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, handlerException(exception, t))
        }
    }

    // use thread's handler
    val currentThread = Thread.currentThread()
    // addSuppressed is never user-defined and cannot normally throw with the only exception being OOM
    // we do ignore that just in case to definitely deliver the exception
    runCatching { exception.addSuppressed(DiagnosticCoroutineContextException(context)) }
    currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
}

遍历一下已经添加到handler列表里面的异常处理器用来处理异常,如果没有会调用当前线程的异常处理器,这部分大概就是这样。

在supervisorJob的例子中,我们发现Job2抛出异常后,异常被myScope异常处理器处理,说明Job2的上下文集合中继承了myScope集合的ExceptionHandler元素,因为supervisorJob只是和父Job关联了,并且launch方法从父Job那里继承元素集合,而我们的Job2是又和supervisorJob组成父子关系,又拥有ExceptionHandler,因此异常发生的时候会用继承过来的异常处理器进行异常处理,如果我们在launch的参数中添加ExceptionHandler话,运行下看下效果:

  var job2 : Job? = launch(SupervisorJob(coroutineContext[Job]) + CoroutineExceptionHandler{ c, e ->
            println("job2 exceptionHandler: " + c[CoroutineName].toString() + e.message)
        }) {
      println("job2 begin")
      delay(100)
      throw Exception("测试异常报错")
      println("job2 end")
  }

打印结果:

2022-11-15 17:22:15.989 E/MainActivity: main : job has launch
2022-11-15 17:22:15.995 E/MainActivity: DefaultDispatcher-worker-1 : job has run to end
2022-11-15 17:22:15.996 E/MainActivity: DefaultDispatcher-worker-1 : job2 begin
2022-11-15 17:22:15.998 E/MainActivity: DefaultDispatcher-worker-1 : job3 begin
2022-11-15 17:22:16.099 E/MainActivity: DefaultDispatcher-worker-2 : job2 exceptionHandler: CoroutineName(name)测试异常报错
2022-11-15 17:22:18.002 E/MainActivity: DefaultDispatcher-worker-2 : job3 end

可以看到Job2的异常被自己添加的job2 exceptionHandler处理了,因为i从父继承过来的ExceptionHandler被自己设置的ExceptionHandler覆盖掉了,其他的运行效果也不变。

Job异常处理的传递链条

首先分析如下代码:

val myScope = CoroutineScope(CoroutineName("name") + IO + Job() + CoroutineExceptionHandler{ c, e ->
            println("CoroutineScope exceptionHandler: " + c[CoroutineName].toString() + e.message)
        })

val job = myScope.launch(CoroutineExceptionHandler{ c, e ->
    println("job exceptionHandler: " + c[CoroutineName].toString() + e.message)
}) {

    var job2 : Job? = launch(CoroutineExceptionHandler{ c, e ->
        println("job2 exceptionHandler: " + c[CoroutineName].toString() + e.message)
    }) {
        println("job2 begin")
        delay(100)
        throw Exception("测试异常报错")
        println("job2 end")
    }

    var job3 : Job? = launch {
        println("job3 begin")
        delay(2000)
        println("job3 end")
    }


    println("job has run to end")
}

println("job has launch")

打印的结果是:

2022-11-15 17:29:27.926 E/MainActivity: main : job has launch
2022-11-15 17:29:27.934 E/MainActivity: DefaultDispatcher-worker-1 : job has run to end
2022-11-15 17:29:27.935 E/MainActivity: DefaultDispatcher-worker-1 : job2 begin
2022-11-15 17:29:27.937 E/MainActivity: DefaultDispatcher-worker-3 : job3 begin
2022-11-15 17:29:28.040 E/MainActivity: DefaultDispatcher-worker-2 : job exceptionHandler: CoroutineName(name)测试异常报错

可以看到,异常处理被Job处理了, 很好奇,为啥不是根部的scope的Job去处理的异常处理啊,卧槽,还得继续跟踪代码,关键还是这个函数fun finalizeFinishingState(state: Finishing, proposedUpdate: Any?): Any?的异常处理的部分:

 //JobSupport
 // Now handle the final exception
 if (finalException != null) {
     val handled = cancelParent(finalException) || handleJobException(finalException)
     if (handled) (finalState as CompletedExceptionally).makeHandled()
 }
 
 //JobSupport
 public open fun childCancelled(cause: Throwable): Boolean {
     if (cause is CancellationException) return true
     return cancelImpl(cause) && handlesException
 }

首先会调用cancelParent(finalException) 尝试让父Job调用childCancelled方法处理异常,这个函数返回false,说明父Job不处理,就会让Job本身去处理异常,job 协程和myScope的Job组成了父子关系,进而会尝试让myScope的Job处理异常,但是myScope的JobhandlesException的字段是false,导致返回的值是false:

  internal open val handlesException: Boolean get() = true
  
  override val handlesException: Boolean = handlesException()

  @JsName("handlesExceptionF")
  private fun handlesException(): Boolean {
      var parentJob = (parentHandle as? ChildHandleNode)?.job ?: return false
      while (true) {
          if (parentJob.handlesException) return true
          parentJob = (parentJob.parentHandle as? ChildHandleNode)?.job ?: return false
      }
  }

mySocpe的handlesException这个字段为啥是false呢?作为根Job,所以它没有父Job,(parentHandle as? ChildHandleNode)?.job ?: return false 它的parentHandle必然是空的,因此导致返回false。这样的话myScope的Job不处理异常,cancelParent(finalException) 函数返回false,导致只能job自己去处理异常调用了handleJobException(finalException)函数,最后进入了job 自己的异常处理器中。

再举一个例子,让job协程不定义自己的异常处理器,看看是哪个处理了异常:

val myScope = CoroutineScope(CoroutineName("name") + IO + Job() + CoroutineExceptionHandler{ c, e ->
            printlnM("CoroutineScope exceptionHandler: " + c[CoroutineName].toString() + e.message)
        })

val job = myScope.launch {

    var job2 : Job? = launch(CoroutineExceptionHandler{ c, e ->
        printlnM("job2 exceptionHandler: " + c[CoroutineName].toString() + e.message)
    }) {
        printlnM("job2 begin")
        delay(100)
        throw Exception("测试异常报错")
        printlnM("job2 end")
    }

    var job3 : Job? = launch {
        printlnM("job3 begin")
        delay(2000)
        printlnM("job3 end")
    }


    printlnM("job has run to end")
}

printlnM("job has launch")
2022-11-15 23:15:12.989  E/coroutineTestActivity: job has launch
2022-11-15 23:15:12.990  E/coroutineTestActivity: job has run to end
2022-11-15 23:15:12.990  E/coroutineTestActivity: job3 begin
2022-11-15 23:15:12.990  E/coroutineTestActivity: job2 begin
2022-11-15 23:15:13.092  E/coroutineTestActivity: CoroutineScope exceptionHandler: CoroutineName(name)测试异常报错

可以看到是myScope exceptionHandler处理的异常,这是因为joblaunch的时候,继承的就是Scope的上下文元素集合,没有传入CoroutineExceptionHandler元素,所以myScope exceptionHandler也不会被覆盖掉,这样job在处理异常的时候使用的myScopeexceptionHandler来处理的异常。

使用范例

想让协程A,B异常互不影响的写法。

  • 正确姿势一:
val scope = CoroutineScope(SupervisorJob() + CoroutineExceptionHandler { _, _ -> })
scope.launch(CoroutineName("A")) {
    delay(10)
    throw RuntimeException()
}
scope.launch(CoroutineName("B")) {
    delay(100)
    Log.e("petterp", "正常执行,我不会收到影响")
}
  • 正确姿势二:
supervisorScope {
    launch(CoroutineName("A")) {
        printlnM("job2 begin")
        delay(100)
        throw Exception("测试异常报错")
        printlnM("job2 end")
    }

    launch(CoroutineName("B")) {
        printlnM("job3 begin")
        delay(2000)
        printlnM("job3 end")
    }
}

上面代码的节点结构:在这里插入图片描述

  • 错误姿势:
val scope = CoroutineScope(CoroutineExceptionHandler { _, _ -> })
scope.launch(CoroutineName("job") + SupervisorJob()) {
    launch(CoroutineName("A")) {
        delay(10)
        throw RuntimeException()
    }
    launch(CoroutineName("B")) {
        delay(100)
        Log.e("petterp", "正常执行,我不会收到影响")
    }
}

scope.launch方法会创建一个job,参数中的SupervisorJob只是覆盖context集合中的父job元素,当前的ob和SupervisorJob仍然会构成父子关系,因此子A和B不是直接和SupervisorJob连接的,异常传递还是会到达job,因此A和B异常会相互影响。
上面节点结构:在这里插入图片描述

总结

  1. 异常的传导链,从子Job向父Job传导:如果父Job是supervisorJob的话,将不做处理,需要子Job自己处理;如果父Job是JobSupport的话,异常还会继续向父Job的父Job传递,直到根部Job。所以捕获异常的话在根ScopeCoroutine里面设置就比较合适,对于有supervisorJob的情况,需要在supervisorJob的子Job中设置异常处理器,supervisorJob自己不会处理异常。

    异常的情况:
    在这里插入图片描述

  2. 对应协程取消操作,子Job取消,不论父Job是supportJob还是supervisorJob,父Job都不会处理cancelledException,父Job不受影响;子Job会主动取消自己以及自己的所有子Job。

    取消的情况:
    在这里插入图片描述

异常的处理逻辑可以用职场的例子解释。假设职场的潜规则是,任何员工出错了,首要是要向上级报告,如果上级愿意处理你的错误,那员工就不用管了,如果上级将问题打回给员工,那错误就得由员工自己处理
那么回到问题本身,Job就相当于一个好老板,子协程犯的错,它愿意处理,SupervisorJob就相当于一个严厉的老板,子协程自己犯的错,自己解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值