作者:Bezier
前言
通常我不喜欢去写分析源码类的文章,流水线式的分析 枯燥乏味,但读完Retrofit源码后让我有了改变这种想法的冲动~~
一般来讲读源码的好处有两点:
- 熟悉代码设计流程,使用过程碰到问题可以更快速解决。说实话仅这一点无法激起我读源码的兴趣,毕竟以正确的姿态使用一个优秀的框架不应该出现这种问题。
- 一个优秀的框架必须要保证易用性、扩展性,所以作者定会引入大量的思考进行设计,如若我们能吸收一二,那何尝不是与作者进行了一次心灵交互呢!
今天我将带着我的理解,尝试从设计者的角度分析Retrofit原理,相信你认真读完再加以思考,当再被面试官问Retrofit时你的答复或许会让他眼前一亮
提示:Retrofit基于2.9.0。文中贴的源码可能会有部分缺失,这是我刻意为之,目的在于筛选掉无用信息增强可读性
目录
- 1. 什么是REST ful API?
- 2. 为什么将请求设置为(接口+注解)形式?
- 2.1. 迪米特法则和门面模式
- 2.2. 为什么通过门面模式设计ApiService?
- 3. 动态代理其实不是工具
- 3.1. Retrofit构建
- 3.2. 何为动态代理?
- 3.3. 动态代理获取ApiService
- 4. ReturnT、ResponseT做一次适配的意义何在?
- 4.1 创建HttpServiceMethod
- 4.2 如何管理callAdapter、responseConverter?
- 4.3 发起请求
1. 什么是REST ful API?
一句话概括REST ful API:在我们使用HTTP协议做数据传输时应当遵守HTTP的规矩,包括请求方法、资源类型、Uri格式等等…
不久前在群里看到某小伙伴提出一个问题:“应后端要求需要在GET请求加入Body但Retrofit 中GET请求添加Body会报错,如何解决?” 一时间讨论的好不热闹,有让把Body塞到Header里的,有让自定义拦截器、也有人直接怂恿改源码…但问题的本质不是后端先违反规则在先吗?两个人打架总不能把挨打的抓起来吧。
俗话说无规矩不成方圆,面对以上这种情况应当让错误方去修改,因为所有人都知道GET没有Body,否则一旦其他人接手你的代码很容易被搞懵。
Retrofit对REST ful API的兼容做的很优秀,不符合规范直接给你报错,强行规范你的代码。所以你们公司正在使用REST ful API而Retrofit将是你的不二选择
2. 为什么将请求设置为(接口+注解)形式?
该小节为前置知识
2.1 迪米特法则和门面模式
迪米特法则:也称之为最小知道原则,即模块之间尽量减少不必要的依赖,即降低模块间的耦合性。
门面模式:基于迪米特法则拓展出来的一种设计模式,旨在将复杂的模块/系统访问入口控制的更加单一。举个例子:现要做一个获取图片功能,优先从本地缓存获取,没有缓存从网络获取随后再加入到本地缓存,假如不做任何处理,那每获取一张图片都要写一遍缓存逻辑,写的越多出错的可能就越高,其实调用者只是想获取一张图片而已,具体如何获取他不需要关心。此时可以通过门面模式将缓存功能做一个封装,只暴露出一个获取图片入口,这样调用者使用起来更加方便而且安全性更高。其实函数式编程也是门面模式的产物
2.2 为什么通过门面模式设计ApiService?
用Retrofit做一次请求大致流程如下:
interface ApiService {
/**
* 获取首页数据
*/
@GET("/article/list/{page}/json")
suspend fun getHomeList(@Path("page") pageNo: Int)
: ApiResponse<ArticleBean>
}
//构建Retrofit
val retrofit = Retrofit.Builder().build()
//创建ApiService实例
val apiService =retrofit.create(ApiService::class.java)
//发起请求(这里用的是suspend会自动发起请求,Java中可通过返回的call请求)
apiService.getHomeList(1)
然后通过Retrofit创建ApiService类型实例调用对应方法即可发起请求。乍一看感觉很普通,但实际上Retrofit通过这种模式(门面模式)帮我们过滤掉了很多无用信息
tips:我们都知道Retrofit只不过是对OkHttp做了封装。
如果直接使用OkHttp,当在构造Request时要做很多繁琐的工作,最要命的是Request可能在多处被构造(ViewModel、Repository…),写的越分散出错时排查的难度就越高。而Retrofit通过注解的形式将Request需要的必要信息全依附在方法上(还是个抽象方法,尽量撇除一切多余信息),作为使用者只需要调用对应方法即可实现请求。至于如何解析、构造、发起请求 Retrofit内部会做处理,调用者不想也不需要知道,
所以Retrofit通过门面模式帮调用者屏蔽了一些无用信息,只暴露出唯一入口,让调用者更专注于业务开发。像我们常用的Room、GreenDao也使用了这种模式
3. 动态代理其实不是工具
看过很多Retrofit相关的文章,都喜欢上来就抛动态代理,关于为什么用只字不提,搞的Retrofit动态代理像是一个工具(框架)一样,殊不知它只是代理模式思想层面的一个产物而已。本小结会透过Retrofit看动态代理本质,帮你解除对它的误解
3.1 Retrofit构建
Retrofit构建如下所示:
Retrofit.Builder()
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(ApiConstants.BASE_URL)
.build()
很典型的构建者模式,可以配置OkHttp、Gson、RxJava等等,最后通过build()做构建操作,跟一下build()代码:
#Retrofit.class
public Retrofit build() {
//1.CallAdapter工厂集合
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.