Android:手把手带你深入剖析 Retrofit 2.0 源码
作者博客
http://www.jianshu.com/u/383970bef0a0
文章目录
前言
简介
与其他网络请求开源库对比
Android-Async-Http
Volley
OkHttp
Retrofit的具体使用
源码分析
Retrofit的本质流程
Retrofit的源代码分析
总结
最后
1
前言
在Android开发中,网络请求十分常用。
而在Android网络请求库中,Retrofit是当下最热的一个网络请求库。
Github截图
今天,我将手把手带你深入剖析Retrofit v2.0的源码,希望你们会喜欢。
在阅读本文前,建议先阅读文章:这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)(http://www.jianshu.com/p/a3e162261ab6)
2
简介
Retrofit简介
特别注意:
准确来说,Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。
原因:网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装。
流程图
App应用程序通过 Retrofit 请求网络,实际上是使用 Retrofit 接口层封装请求参数、Header、Url 等信息,之后由 OkHttp 完成后续的请求操作。
在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,Retrofit根据用户的需求对结果进行解析。
3
与其他网络请求开源库对比
除了Retrofit,如今Android中主流的网络请求框架有:
Android-Async-Http
Volley
OkHttp
下面是简单介绍:
网络请求加载 - 介绍
一图让你了解全部的网络请求库和他们之间的区别!
网络请求库 - 对比
4
Retrofit 的具体使用
具体请看我写的文章:
这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)
(http://www.jianshu.com/p/a3e162261ab6)
5
源码分析
1.Retrofit的本质流程
一般从网络通信过程如下图:
网络请求的过程
其实Retrofit的本质和上面是一样的套路
只是Retrofit通过使用大量的设计模式进行功能模块的解耦,使得上面的过程进行得更加简单 & 流畅。
如下图:
Retrofit的本质
具体过程解释如下:
1.通过解析 网络请求接口的注解 配置 网络请求参数
2.通过 动态代理 生成 网络请求对象
3.通过 网络请求适配器 将 网络请求对象 进行平台适配
平台包括:Android、Rxjava、Guava和java8
4.通过 网络请求执行器 发送网络请求
5.通过 数据转换器 解析服务器返回的数据
6.通过 回调执行器 切换线程(子线程 ->>主线程)
7.用户在主线程处理返回结果
下面介绍上面提到的几个角色
角色说明
特别注意:因下面的 源码分析 是根据 使用步骤 逐步带你debug进去的,所以必须先看文章这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)
2.源码分析
先来回忆Retrofit的使用步骤:
1.创建Retrofit实例
2.创建 网络请求接口实例 并 配置网络请求参数
3.发送网络请求
封装了 数据转换、线程切换的操作
4.处理服务器返回的数据
创建Retrofit实例
a.使用步骤
b. 源码分析
Retrofit实例是使用建造者模式通过Builder类进行创建的。
建造者模式:将一个复杂对象的构建与表示分离,使得用户在不知道对象的创建细节情况下就可以直接创建复杂的对象。具体请看文章:建造者模式(Builder Pattern)- 最易懂的设计模式解析。
(http://www.jianshu.com/p/be290ccea05a)
接下来,我将分五个步骤对创建Retrofit实例进行逐步分析
分析步骤
步骤1
步骤1
成功建立一个Retrofit对象的标准:配置好Retrofit类里的成员变量,即配置好:
serviceMethod:包含所有网络请求信息的对象
baseUrl:网络请求的url地址
callFactory:网络请求工厂
adapterFactories:网络请求适配器工厂的集合
converterFactories:数据转换器工厂的集合
callbackExecutor:回调方法执行器
所谓xxxFactory、“xxx工厂”其实是设计模式中工厂模式的体现:将“类实例化的操作”与“使用对象的操作”分开,使得使用者不用知道具体参数就可以实例化出所需要的“产品”类。
这里详细介绍一下:CallAdapterFactory:该Factory生产的是CallAdapter,那么CallAdapter又是什么呢?
CallAdapter详细介绍
定义:网络请求执行器(Call)的适配器
Call在Retrofit里默认是OkHttpCall
在Retrofit中提供了四种CallAdapterFactory: ExecutorCallAdapterFactory(默认)、GuavaCallAdapterFactory、Java8CallAdapterFactory、RxJavaCallAdapterFactory
作用:将默认的网络请求执行器(OkHttpCall)转换成适合被不同平台来调用的网络请求执行器形式。
如:一开始Retrofit只打算利用OkHttpCall通过ExecutorCallbackCall切换线程;但后来发现使用Rxjava更加方便(不需要Handler来切换线程)。想要实现Rxjava的情况,那就得使用RxJavaCallAdapterFactoryCallAdapter将OkHttpCall转换成Rxjava(Scheduler):
Retrofit还支持java8、Guava平台。
好处:用最小代价兼容更多平台,即能适配更多的使用场景
所以,接下来需要分析的步骤2、步骤3、步骤4、步骤4的目的是配置好上述所有成员变量。
步骤2
步骤2
我们先来看Builder类
请按下面提示的步骤进行查看
对Builder类分析完毕,总结:Builder设置了默认的
平台类型对象:Android
网络请求适配器工厂:CallAdapterFactory
CallAdapter用于对原始Call进行再次封装,如Call<R>到Observable<R>
数据转换器工厂: converterFactory
回调执行器:callbackExecutor
特别注意,这里只是设置了默认值,但未真正配置到具体的Retrofit类的成员变量当中。
步骤3
步骤3
还是按部就班按步骤来观看
至此,步骤3分析完毕
总结:baseUrl()用于配置Retrofit类的网络请求url地址
将传入的String类型url转化为适合OKhttp的HttpUrl类型的url
步骤4
步骤4
我们从里往外看,即先看GsonConverterFactory.creat()
所以,GsonConverterFactory.creat()是创建了一个含有Gson对象实例的GsonConverterFactory,并返回给addConverterFactory()
接下来继续看:addConverterFactory()
至此,分析完毕。
总结:步骤4用于创建一个含有Gson对象实例的GsonConverterFactory并放入到数据转换器工厂converterFactories里。
即Retrofit默认使用Gson进行解析
若使用其他解析方式(如Json、XML或Protocobuf),也可通过自定义数据解析器来实现(必须继承 Converter.Factory)
步骤5
步骤5
终于到了最后一个步骤了。
至此,步骤5分析完毕
总结:在最后一步中,通过前面步骤设置的变量,将Retrofit类的所有成员变量都配置完毕。
所以,成功创建了Retrofit的实例
小结
Retrofit使用建造者模式通过Builder类建立了一个Retrofit实例,具体创建细节是配置了:
平台类型对象(Platform - Android)
网络请求的url地址(baseUrl)
网络请求工厂(callFactory)
默认使用OkHttpCall
网络请求适配器工厂的集合(adapterFactories)
本质是配置了网络请求适配器工厂- 默认是ExecutorCallAdapterFactory
数据转换器工厂的集合(converterFactories)
本质是配置了数据转换器工厂
回调方法执行器(callbackExecutor)
默认回调方法执行器作用是:切换线程(子线程 - 主线程)
由于使用了建造者模式,所以开发者并不需要关心配置细节就可以创建好Retrofit实例,建造者模式get。
在创建Retrofit对象时,你可以通过更多更灵活的方式去处理你的需求,如使用不同的Converter、使用不同的CallAdapter,这也就提供了你使用RxJava来调用Retrofit的可能。
2.创建网络请求接口的实例
a.使用步骤
b.源码分析
结论:Retrofit是通过外观模式 & 代理模式 使用create()方法创建网络请求接口的实例(同时,通过网络请求接口里设置的注解进行了网络请求参数的配置)
下面主要分析步骤3和步骤4:
步骤3讲解:AccessApi NetService = retrofit.create(NetService.class);
创建网络接口实例用了外观模式 & 代理模式:
使用外观模式进行访问,里面用了代理模式
外观模式
外观模式:定义一个统一接口,外部与通过该统一的接口对子系统里的其他接口进行访问。
Retrofit对象的外观(门店) = retrofit.create()
通过这一外观方法就可以在内部调用各个方法创建网络请求接口的实例和配置网络请求参数
大大降低了系统的耦合度
代理模式
代理模式:通过访问代理对象的方式来间接访问目标对象。
分为静态代理 & 动态代理:
静态代理:代理类在程序运行前已经存在的代理方式。
动态代理:代理类在程序运行前不存在、运行时由程序动态生成的代理方式。
return (T) roxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler)通过代理模式中的动态代理模式,动态生成网络请求接口的代理类,并将代理类的实例创建交给InvocationHandler类 作为具体的实现,并最终返回一个动态代理对象。
生成实例过程中含有生成实现类的缓存机制(单例模式),下面会详细分析
使用动态代理的好处:
当NetService对象调用getCall()接口中方法时会进行拦截,调用都会集中转发到 InvocationHandler#invoke (),可集中进行处理
获得网络请求接口实例上的所有注解
更方便封装ServiceMethod
下面看源码分析
下面将详细分析InvocationHandler类 # invoke()里的具体实现
下面将详细介绍3个关注点的代码。
关注点1: ServiceMethod serviceMethod = loadServiceMethod(method);
下面,我将分3个步骤详细分析serviceMethod实例的创建过程:
Paste_Image.png
步骤1:ServiceMethod类 构造函数
Paste_Image.png
步骤2:ServiceMethod的Builder()
Paste_Image.png
步骤3:ServiceMethod的build()
Paste_Image.png
当选择了RxjavaCallAdapterFactory后,Rxjava通过策略模式选择对应的adapter。
具体过程是:根据网络接口方法的返回值类型来选择具体要用哪种CallAdapterFactory,然后创建具体的CallAdapter实例
采用工厂模式使得各功能模块高度解耦
上面提到了两种工厂:CallAdapter.Factory & Converter.Factory分别负责提供不同的功能模块
工厂负责如何提供、提供何种功能模块
Retrofit 只负责提供选择何种工厂的决策信息(如网络接口方法的参数、返回值类型、注解等)
这正是所谓的高内聚低耦合,工厂模式get。
终于配置完网络请求参数(即配置好ServiceMethod对象)。接下来将讲解第二行代码:okHttpCall对象的创建
第二行:OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
根据第一步配置好的ServiceMethod对象和输入的请求参数创建okHttpCall对象
第三行:return serviceMethod.callAdapter.adapt(okHttpCall);
将第二步创建的OkHttpCall对象传给第一步创建的serviceMethod对象中对应的网络请求适配器工厂的adapt()
返回对象类型:Android默认的是Call<>;若设置了RxJavaCallAdapterFactory,返回的则是Observable<>
采用了装饰模式:ExecutorCallbackCall = 装饰者,而里面真正去执行网络请求的还是OkHttpCall
使用装饰模式的原因:希望在OkHttpCall发送请求时做一些额外操作。这里的额外操作是线程转换,即将子线程切换到主线程
OkHttpCall的enqueue()是进行网络异步请求的:当你调用OkHttpCall.enqueue()时,回调的callback是在子线程中,需要通过Handler转换到主线程进行回调。ExecutorCallbackCall就是用于线程回调;
当然以上是原生Retrofit使用的切换线程方式。如果你用Rxjava,那就不会用到这个ExecutorCallbackCall而是RxJava的Call,此处不过多展开
步骤4讲解:Call<JavaBean> call = NetService.getCall();
NetService对象实际上是动态代理对象Proxy.newProxyInstance()(步骤3中已说明),并不是真正的网络请求接口创建的对象
当NetService对象调用getCall()时会被动态代理对象Proxy.newProxyInstance()拦截,然后调用自身的InvocationHandler # invoke()
invoke(Object proxy, Method method, Object... args)会传入3个参数:Object proxy:(代理对象)、
Method method(调用的getCall())
Object... args(方法的参数,即getCall(*)中的*)接下来利用Java反射获取到getCall()的注解信息,配合args参数创建ServiceMethod对象。
如上面步骤3描述,此处不再次讲解
最终创建并返回一个OkHttpCall类型的Call对象。
OkHttpCall类是OkHttp的包装类
创建了OkHttpCall类型的Call对象还不能发送网络请求,需要创建Request对象才能发送网络请求
总结
Retrofit采用了外观模式统一调用创建网络请求接口实例和网络请求参数配置的方法,具体细节是:
动态创建网络请求接口的实例(代理模式 - 动态代理)
创建 serviceMethod 对象(建造者模式 & 单例模式(缓存机制))
对 serviceMethod 对象进行网络请求参数配置:通过解析网络请求接口方法的参数、返回值和注解类型,从Retrofit对象中获取对应的网络请求的url地址、网络请求执行器、网络请求适配器 & 数据转换器。(策略模式)
对 serviceMethod 对象加入线程切换的操作,便于接收数据后通过Handler从子线程切换到主线程从而对返回数据结果进行处理(装饰模式)
最终创建并返回一个OkHttpCall类型的网络请求对象。
3. 执行网络请求
Retrofit默认使用OkHttp,即OkHttpCall类(实现了 retrofit2.Call<T>接口)
但可以自定义选择自己需要的Call类
OkHttpCall提供了两种网络请求方式:
同步请求:OkHttpCall.execute()
异步请求:OkHttpCall.enqueue()
下面将详细介绍这两种网络请求方式。
对于OkHttpCall的enqueue()、execute()此处不往下分析,有兴趣的读者可以看OkHttp的源码
同步请求OkHttpCall.execute()
发送请求过程
步骤1:对网络请求接口的方法中的每个参数利用对应ParameterHandler进行解析,再根据ServiceMethod对象创建一个OkHttp的Request对象。
步骤2:使用OkHttp的Request发送网络请求;
步骤3:对返回的数据使用之前设置的数据转换器(GsonConverterFactory)解析返回的数据,最终得到一个Response<T>对象
具体使用
上面简单的一行代码,其实包含了整个发送网络同步请求的三个步骤。
源码分析
特别注意:
ServiceMethod几乎保存了一个网络请求所需要的数据。
发送网络请求时,OkHttpCall需要从ServiceMethod中获得一个Request对象。
解析数据时,还需要通过ServiceMethod使用Converter(数据转换器)转换成Java对象进行数据解析。
为了提高效率,Retrofit还会对解析过的请求ServiceMethod进行缓存,存放在Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();对象中,即第二步提到的单例模式。
关于状态码检查时的状态码说明:
Paste_Image.png
以上便是整个以同步的方式发送网络请求的过程。
异步请求
发送请求过程
步骤1:对网络请求接口的方法中的每个参数利用对应ParameterHandler进行解析,再根据ServiceMethod对象创建一个OkHttp的Request对象;
步骤2:使用OkHttp的Request发送网络请求;
步骤3:对返回的数据使用之前设置的数据转换器(GsonConverterFactory)解析返回的数据,最终得到一个Response<T>对象;
步骤4:进行线程切换从而在主线程处理返回的数据结果。
若使用了RxJava,则直接回调到主线程
异步请求的过程跟同步请求类似,唯一不同之处在于:异步请求会将回调方法交给回调执行器在指定的线程中执行。
指定的线程此处是指主线程(UI线程)
具体使用
从上面分析有:call是一个静态代理
使用静态代理的作用是:在okhttpCall发送网络请求的前后进行额外操作
这里的额外操作是:线程切换,即将子线程切换到主线程,从而在主线程对返回的数据结果进行处理。
源码分析
以上便是整个以异步方式发送网络请求的过程。
6
总结
Retrofit 本质上是一个 RESTful 的HTTP 网络请求框架的封装,即通过 大量的设计模式 封装了 OkHttp ,使得简洁易用。具体过程如下:
Retrofit 将 Http请求 抽象 成 Java接口
在接口里用 注解 描述和配置 网络请求参数
用动态代理 的方式,动态将网络请求接口的注解 解析 成HTTP请求
最后执行HTTP请求
最后贴一张非常详细的Retrofit源码分析图:
Retrofit源码分析图
7
总结
看完本文,相信你已经非常熟悉 Retrofit 2.0 的源码分析;
关于Retrofit 2.0的详细使用教程,请看文章这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解);
接下来,我将继续分析与 Retrofit 配合使用的 RxJava,有兴趣可以继续关注。
今日推荐