优雅使用Retrofit,在协程时代遨游安卓网络请求(一)上

简介: 优雅使用Retrofit,在协程时代遨游安卓网络请求(一)

前言:由于框架本身也在不断地迭代,因此文章中的部分代码可能存在更新或者过时,如果你想阅读源码或者查看代码的在项目中的实际使用方法,可以查看笔者目前在维护的compose项目:Spacecraft: 《Spacecraft - 我的安卓技术实践平台》-查看代码请进入develop分支 (gitee.com)


Retrofit框架之美


  在众多安卓网络请求框架中,Retrofit无疑是最耀眼的那个:基于okhttp实现,迁移成本低、API设计简洁易用、注解化配置高度解耦、支持多种解析器、支持rxjava和协程。。。

  然而实际项目中,Retrofit有诸多不便,例如:返回值Call容易陷入嵌套地域、网上大多数CallAdapter方案需要手动try-catch、缺乏全局逻辑等。不过好消息是,得益于Retrofit强大的定制功能,我们可以逐一解决上诉的痛点,让Retrofit从一个花瓶变成一个具有实战意义的工具。

我们的目标是让Retrofit拥有以下功能和特性:

  • 在协程下执行网络请求,让异步请求代码变成近似同步代码,消除嵌套地狱
  • 网络API的注解自定义和逻辑自定义
  • 增加可配置的全局结果转换器,例如返回的成功报文中,含有code为非0的结果,需要转换成错误的报文
  • 网络请求方法不用手动try-catch异常,即方法不向上抛出,方法返回值包含网络异常

阅读本系列需要以下知识点:

  • kotlin
  • Hilt(最好了解)
  • Retrofit(基础或者使用过即可)
  • okhttp(了解拦截器的使用)

image.png


一:分析问题


  默认情况下,Retrofit支持Call的返回值,我们需要调用Call类中的方法来请求网络,然后通过回调来获取网络请求的成功和失败的结果。


interface IGetRequest {
        @GET("xxxxxx")
        fun getStudent(): Call<StudentBean>
    }
    call.enqueue(object : Callback<User> {
            override fun onResponse(call: Call<User>, response: Response<User>) {
            }
            override fun onFailure(call: Call<User>, t: Throwable) {
            }
        })
    }

  这个显然是不符合我们的要求的,因为我们要消除嵌套地狱,我们希望Retrofit直接把网络请求的“结果”返回给调用者,即整个请求是“阻塞”的,即在协程作用域中“挂起”。

  需要注意的一点是,这里的“结果”并不是指网络请求成功的结果,而是指网络请求结束的结果,即结果包含了“成功”、“异常”、“服务器内部错误”三种情况,我们只需要判断结果的类型即可

  因此,大致需要设计一个这样的api。


interface IGetRequest {
        @GET("xxxxxx")
        suspend fun getStudent(): Result<StudentBean>
}
//getStudent()是异步请求,但是在协程作用域中挂起了,因此看起来像是同步代码
val result=iGetRequest.getStudent()


二:定义NetworkResult


  废话不多说,先看代码(实体类内部隐藏了部分逻辑代码,例如如何处理response成员属性,会在下篇文章中讲解,本篇重点不在此处因此省略)。


sealed class NetworkResult<T> {
    /**
     * 网络请求成功
     */
    class Success<T>(private val response: Response<T>) : NetworkResult<T>(){}
    /**
     * 网络请求失败
     */
    sealed class Failure<T> : NetworkResult<T>() {
        /**
         * 服务器内部错误
         */
        data class ServerError<T>(private val response: Response<T>) : Failure<T>() {}
        /**
         * 网络请求出现异常
         */
        data class Exception<T> constructor(
            val exception: Throwable
        ) : Failure<T>() {}
    }
}

  利用kotlin密封类的特性(子类数量是有限的,可以通过枚举把所有子类遍历),总共定义5个类型,不过真正使用的其实是SuccessServerErrorException三个。

  • Success指的是网络请求成功
  • ServerError指的是Http协议报文中的状态码不在200-300区间内,通常为404等
  • Exception指的是网络请求过程中发生了异常,例如超时异常,网络断开异常,实体类解析异常等

问:为什么不直接设置成功和失败两种类型,而是要给失败添加两个子类?

答:因为本质上,服务器内部错误并不是发生了异常,而是服务器返回了报文,只是这个报文在协议层面是错误而已,当然你也可以将服务器内部错误转成某个IO异常,我不希望这样做,因为这样容易混淆两者,在某些特殊的场景下会让问题更加难以处理。

  接下来,我们修改接口,将方法返回值改为NetworkResult


interface FriendService {
    @POST("/friend/list")
    suspend fun requestFriend(
        @Body friendRequestParam: FriendRequestParam
    ): NetworkResult<Friends>
    data class FriendRequestParam(
        private val page: String,
    )
}

  不幸的是,如果你直接这样做,Retrofit将会直接崩溃,因为道理也很简单,Retrofit只知道如何处理返回Call,而不知道如何返回我们自己定义的NetworkResult。

image.png



相关文章
|
6月前
|
缓存 Java API
[AIGC] OkHttp:轻松实现网络请求
[AIGC] OkHttp:轻松实现网络请求
|
缓存 JSON API
XHttp2 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp进行组装
XHttp2 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp进行组装
698 0
XHttp2 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp进行组装
|
Android开发
优雅使用Retrofit,在协程时代遨游安卓网络请求(一)下
优雅使用Retrofit,在协程时代遨游安卓网络请求(一)
92 0
|
JSON 程序员 Android开发
优雅使用Retrofit,在协程时代遨游安卓网络请求(二)上
优雅使用Retrofit,在协程时代遨游安卓网络请求(二)
77 0
|
数据安全/隐私保护 Android开发
优雅使用Retrofit,在协程时代遨游安卓网络请求(二)下
优雅使用Retrofit,在协程时代遨游安卓网络请求(二)
149 0
|
API Android开发
优雅使用Retrofit,在协程时代遨游安卓网络请求(三)下
优雅使用Retrofit,在协程时代遨游安卓网络请求(三)
69 0
|
自然语言处理 程序员 Android开发
优雅使用Retrofit,在协程时代遨游安卓网络请求(三)上
优雅使用Retrofit,在协程时代遨游安卓网络请求(三)
126 0
|
缓存 JSON 网络协议
打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约1
打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约
307 0
|
JSON 缓存 前端开发
打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约2
打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约
229 0
|
网络协议 Java
OkHttp架构—异步请求enqueue(不完整篇)
我分为了四个部分,橙色第一部分实例化一个OkHttoClient类对象就可以了。 所有的逻辑大部分在拦截器Interceptors中,但进入拦截器之前还要靠分发器来调配请求任务。 分发器Dispatcher:内部维护队列和线程池,完成请求调配。 拦截器Interceptors:完成整个请求。
568 0