解决firebase 上的一个bug

requestLocationWithPermissions 是一个suspend 函数,页面destory 后,
searchPositionVm.mapController?.animateMyLocationMarker(location) 还执行,报错Can't access ViewModels from detached fragment

以下是修改好的,发现是try catch 但是没有重新抛出CancellationException

机制概述

Kotlin协程的取消机制基于异常抛出实现:当协程被取消时,会抛出CancellationException异常。该异常被抛出后,代码执行流将立即跳转到最近的catch块,后续代码不再执行。

源码执行流程分析

1. 挂起函数恢复执行点

suspend fun cc函数中,挂起函数恢复执行的关键位置是resumeWith方法调用处,这是协程恢复执行的入口。

2. 结果获取与异常判断

在获取挂起函数返回结果时,系统会检查结果是否为异常导致的失败:

  • 如果是异常情况,直接抛出异常

  • invokeSuspend方法随即结束,后续代码不再执行

  • 异常抛出实际发生在suspend函数返回结果时

3. 异常捕获与传递

外层try-catch块会捕获异常并进行封装,然后将异常传递给父协程,形成异常传播链。

4. 取消状态检查

在执行过程中,会先检查协程是否已被取消:

  • 如果已取消,则调用continuation.resumeWithStackTrace(cause)

  • 此机制确保取消信号能正确传递

关键实现点

异常的实际生成发生在DispatchedTask.run()方法中。Job取消时,取消事件从父协程向子协程逐层传递。

重要结论与最佳实践

不要在suspend函数外部使用try-catch捕获所有异常,因为这会:

  • 意外捕获CancellationException

  • 将异常转换为result.success结果

  • 破坏异常向上传递的机制

  • 影响协程正常的取消流程

建议仅在明确需要处理非取消异常时,在suspend函数内部使用try-catch,且应重新抛出CancellationException

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐