一.异常处理-try-catch
1.简介
try-catch在Java中是一种经典的处理异常的方式,在Kotlin中也可以使用。
2.try-catch
try {//可能抛出异常的代码
......
} catch (e: Exception) {//异常处理
......
} finally {//无论是否异常都会执行的代码
......
}
3.举例
以两个整数相除为例,被除数为0时报错
val divisor = 100
val dividend = 0
val result = try {
divisor / dividend
} catch (e: Exception) {
Log.d("协程测试", "异常信息:${e.message}")
} finally {
Log.d("协程测试", "finally方法...")
}
Log.d("协程测试", "100/0=$result")
4.结果
异常信息:divide by zero
finally方法...
100/0=1
5.结论
故意将被除数入参0,使用try-catch捕获异常,结果如上,虽然没有报错,但是返回的结果是错误的,这显然是不有好的。
二.异常处理-runCatching
1.简介
runCatching是Kotlin较Java特有的一种处理异常的方式,它返回一个Result<T>对象,把异常处理变成了可以链式调用的操作。
2.源码
@InlineOnly
@SinceKotlin("1.3")
public inline fun <T, R> T.runCatching(block: T.() -> R): Result<R> {
return try {
Result.success(block())
} catch (e: Throwable) {
Result.failure(e)
}
}
runCatching有两个回调的方法,success&failure。
success:返回正确的内容。没有异常,返回正确值。
failure:返回错误的内容,主要返回异常信息。
3.举例
private fun calculate(divisor: Int, dividend: Int): Result<Int> {
return runCatching {
divisor / dividend
}
}
//正确举例 正确时不做其他处理
val num1 = calculate(100, 5)
num1.onSuccess { content ->
Log.d("协程测试", "正确 100/5=$content")
}.onFailure { exception ->
Log.d("协程测试", "错误 100/5= ${exception.message}")
}
//正确举例 正确时修改返回值内容 即输出100/5=21
val num2 = calculate(100, 5)
num2.map { content ->//正确时,操作返回值内容,在onSuccess方法之前执行,走onSuccess回调
content + 1
}.onSuccess { content ->
Log.d("协程测试", "正确 100/5=$content")
}.onFailure { exception ->
Log.d("协程测试", "错误 100/5= ${exception.message}")
}
//错误举例 错误时不做其他处理
val num3 = calculate(100, 0)
num3.onSuccess { content ->
Log.d("协程测试", "正确 100/0=$content")
}.onFailure { exception ->
Log.d("协程测试", "错误 100/0= ${exception.message}")
}
//错误举例 不错误时,强制返回20 即输出100/0=20
val num4 = calculate(100, 0)
num4.recover {//失败时,操作返回值内容,在onSuccess方法之前执行,走onSuccess回调
20
}.onSuccess { content ->
Log.d("协程测试", "正确 100/0=$content")
}.onFailure { exception ->
Log.d("协程测试", "错误 100/0= ${exception.message}")
}
4.结果
正确 100/5=20
正确 100/5=21
错误 100/0= divide by zero
正确 100/0=20
5.结论
上述打印的结果可知,runCatching处理异常相当的简单。
<1>.无异常时,回调onSuccess 方法,返回正常的结果。如果想要在无异常时,操作一下返回值,可以在onSuccess方法前重写map方法做些额外的操作。比如
.map { content ->//正确时,操作返回值内容,在onSuccess方法之前执行,走onSuccess回调
content + 1
}
map方法源码
@InlineOnly
@SinceKotlin("1.3")
public inline fun <R, T> Result<T>.map(transform: (value: T) -> R): Result<R> {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when {
isSuccess -> Result.success(transform(value as T))
else -> Result(value)
}
}
在onSuccess方法前重写,可以覆盖原本正确的结果,然后在onSuccess方法中回调。
<2>.有异常时,回调onFailure 方法,返回异常信息。如果想要在有异常时,操作一下返回值,可以在onSuccess方法前重写recover方法,做些额外的操作,使其强制执行onSuccess方法,而不执行onFailure方法。比如
.recover {//失败时,操作返回值内容,在onSuccess方法之前执行,走onSuccess回调
20
}
recover源码
@InlineOnly
@SinceKotlin("1.3")
public inline fun <R, T : R> Result<T>.recover(transform: (exception: Throwable) -> R): Result<R> {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (val exception = exceptionOrNull()) {
null -> this
else -> Result.success(transform(exception))
}
}
在onSuccess方法前重写,可以让原本发生异常时走onFailure方法改成强制执行onSuccess方法。
6.补充
Result还有几个其他的方法。
<1>.isSuccess / isFailure:用于检查结果是成功还是失败的布尔值。
val num2 = calculate(100, 5)
num2.map { content ->//正确时,操作返回值内容,在onSuccess方法之前执行,走onSuccess回调
content + 1
}.onSuccess { content ->
Log.d("协程测试", "正确 100/5=$content")
}.onFailure { exception ->
Log.d("协程测试", "错误 100/5= ${exception.message}")
}
val isSuccess = num2.isSuccess
Log.d("协程测试", "isSuccess= $isSuccess")
****************************输出***********************************************
正确 100/5=21
isSuccess= true
val num3 = calculate(100, 0)
num3.onSuccess { content ->
Log.d("协程测试", "正确 100/0=$content")
}.onFailure { exception ->
Log.d("协程测试", "错误 100/0= ${exception.message}")
}
val isFailure = num3.isFailure
Log.d("协程测试", "isFailure= $isFailure")
***************************************输出**********************************
错误 100/0= divide by zero
isFailure= true
<2>.getOrNull():如果操作成功,返回对应的值;如果失败,则返回 null。
<3>.exceptionOrNull():如果操作失败,返回抛出的异常;如果成功,则返回 null。