面试题答案
一键面试1. Kotlin协程异常处理机制与常规try - catch的不同
- 传播方式:
- 常规try - catch:直接在代码块内捕获异常,异常不会跨越函数边界自动传播,除非手动抛出。例如:
fun normalFunction() {
try {
// 可能抛出异常的代码
val result = 1 / 0
} catch (e: ArithmeticException) {
// 捕获并处理异常
println("捕获到算术异常: $e")
}
}
- **Kotlin协程**:异常在协程作用域内会自动传播,子协程抛出的异常会向上传递给父协程。如果没有在协程内部处理,会导致整个协程作用域取消。例如:
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
val job = launch {
throw RuntimeException("子协程异常")
}
job.join()
}.join()
println("主协程结束")
}
// 这里子协程抛出异常,没有捕获,导致整个协程作用域取消,不会打印 "主协程结束"
- 处理位置:
- 常规try - catch:在可能抛出异常的代码周围使用try - catch块进行捕获。
- Kotlin协程:除了在协程内部使用try - catch块,还可以在协程构建器(如
launch
、async
)中通过CoroutineExceptionHandler
来处理异常。
2. 在协程中正确处理异常
2.1 使用try - catch块
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
try {
// 可能抛出异常的代码
val result = 1 / 0
} catch (e: ArithmeticException) {
// 捕获并处理异常
println("捕获到算术异常: $e")
}
}.join()
println("主协程结束")
}
// 这里在协程内部使用try - catch捕获异常,主协程会正常结束并打印 "主协程结束"
2.2 使用CoroutineExceptionHandler
import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { _, exception ->
println("全局捕获异常: $exception")
}
fun main() = runBlocking(handler) {
launch {
throw RuntimeException("协程异常")
}.join()
println("主协程结束")
}
// 通过CoroutineExceptionHandler在协程作用域级别捕获异常,主协程会正常结束并打印 "主协程结束"
2.3 处理取消异常
取消异常(CancellationException
)在协程被取消时抛出。通常不需要手动捕获处理,因为协程取消是正常的流程控制。但如果确实需要区分,可以如下处理:
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
try {
delay(1000)
} catch (e: CancellationException) {
println("协程被取消: $e")
throw e
}
}
delay(500)
job.cancel()
job.join()
println("主协程结束")
}
// 这里手动捕获取消异常,并打印相关信息,主协程会正常结束并打印 "主协程结束"