前言:由于框架本身也在不断地迭代,因此文章中的部分代码可能存在更新或者过时,如果你想阅读源码或者查看代码的在项目中的实际使用方法,可以查看笔者目前在维护的compose项目:Spacecraft: 《Spacecraft - 我的安卓技术实践平台》-查看代码请进入develop分支 (gitee.com)
本篇内容主要为实现网络框架中与全局相关的逻辑。
网络异常转自然语言信息
在上一节中,我们遗留了部分代码未提供,这里我们回顾一下
/** * 网络请求出现异常 */ data class Exception<T>(val exception: Throwable) : Failure<T>() { //分析异常类型,返回自然语言错误信息 val exceptionMessage:String by lazy { //TODO 下文讲解 } }
这个自然语言的意思是:用户能直接阅读的错误信息,我们换个角度想一下,如果程序直接把网络超时异常SocketTimeoutException的信息抛出给用户,例如一个Toast,会发生什么事呢?可想而知用户会感到非常的困惑(前提是他不是一个程序员),因此需要将粗暴的程序错误堆栈信息转换成人可读的信息。
依然是废话不多说!上代码。
/** * 网络异常解析器 */ interface HttpExceptionParser{ /** * 将一个异常转化为自然语言报错 */ fun parse(throwable: Throwable?): String /** * 默认实现 */ companion object{ val DEFAULT_PARSER= object : HttpExceptionParser { override fun parse(throwable: Throwable?): String { return when (throwable) { is ConnectException, is SocketException, is HttpException, is UnknownHostException -> "网络连接失败" is SSLHandshakeException -> "证书验证失败" is JSONException, is ParseException, is JsonParseException -> "解析报文失败" is SocketTimeoutException -> "连接超时" is MsgException -> throwable.tip else -> "未知错误" } } } } }
代码非常的简单,就是一个接口中包含了一个方法,将throwable转成字符串,然后接口中包含一个默认实现(这里可以改成你想要的)
接下来,我们再实现一个Holder去持有Parser,让Holder实现Parser的方法(代理者模式),然后调用真正的parser去解析(你可以更换parser去实现你想要的自定义逻辑)
/** * 异常处理器持有者 */ object HttpExceptionParserHolder:HttpExceptionParser { var parser=DEFAULT_PARSER override fun parse(throwable: Throwable?): String { return parser.parse(throwable) } }
这样,回到最初的代码,我们把Holder传入进去,完成逻辑闭环,用户就再也不会看到奇奇怪怪的网络异常报错了。
/** * 网络请求出现异常 */ data class Exception<T>(val exception: Throwable) : Failure<T>() { val exceptionMessage: String by lazy { HttpExceptionParserHolder.parse(exception) } }
NetworkResult拦截器
在本网络框架的网络请求中,在服务器无异常且拿到服务器报文后,逻辑会执行到Success中去,但是有时候我们有这样的需求,报文中有个字段,例如Code,如果Code不为1,则说明业务报错,因此我们还需要进一步判断Code的值,否则会出现BUG。
那么问题来了,我们如果实现全局逻辑而不是在具体的某个请求的Success中回调呢? 笔者参考过很多人的解决方案,大部分是对OKhttp的拦截器进行处理,即在okhttp拦截器中进行校验,但是这样有个缺陷是,你需要多执行一遍实体类型的转换(因为Retrofit层已经有一层),性能差而且存在代码维护的难度(因为存在两套实体类转换逻辑,okhttp和retrofit各一套),因此我们希望统一在Retrofit层面进行校验。
让我们回到第一节的代码(第一节传送门:优雅使用Retrofit,在协程时代遨游安卓网络请求(一) - 掘金 (juejin.cn))
在第一节中,笔者提供了一个将Retrofit的Response转成NetworkResult的扩展方法,我们的关键点就是这里。
fun <T> Response<T>.toNetworkResult(): NetworkResult<T> = //此处可以做全局转换 try { if (isSuccessful) { toSuccessResult() } else { toServerErrorResult() } } catch (t: Throwable) { t.toExceptionResult() }
这个方法的返回值是NetworkResult,因此可以返回Success,Failure中的任意一种,我们只需要在这里插入转换的逻辑,将部分Success的转成Failure即可(当然你可以实现任意的转换,甚至将错误转成正确都可以,非常的任性)。
那么如何实现的,废话不多说,直接上代码!
interface GlobalNetworkResultInterceptor { /** * 拦截网络请求, */ fun <T> onIntercept(networkResult: NetworkResult<T>): NetworkResult<T> { return networkResult } //默认实现 object DefaultGlobalNetworkResultInterceptor : GlobalNetworkResultInterceptor }
和异常解析器非常类似,也是一接口带着一个默认实现,方法就是将一个NetworkResult变成另外一个NetworkResult,默认就是不转。