众所周知,Retrofit是OkHttp的封装,APP对网络交互部分的实现基本上都是RxJava+Retrofit+OkHttp架构(或协程+Retrofit+OkHttp),可以说,Retrofit已经广为人知。本文主要介绍Retrofit主线源码实现机制,及其所采用的设计模式所涉及到的思想等等。
OKHttp 的使用缺陷
这里说的使用缺陷是不那么便利的地方:
1)用户网络请求的接口配置繁琐,尤其是需要配置复杂请求body,请求头,参数的时候;
2)数据解析过程需要用户手动拿到responsbody进行解析,不能复用;
3)无法适配自动进行线程的切换;
4)万一我们的存在嵌套网络请求就会陷入“回调陷阱”。
Retrofit主要解决的问题
在OkHttp使用如此繁琐的情况下,更方便使用的Retrofit应运而生。其所解决的OkHttp的缺陷问题包含两方面:
①请求前:完成统一配置的网络请求头,一致适配请求request。
②结果返回:retrofit完成数据适配、线程切换。
简单使用
(本段文字仅讲述retrofit的基本用法,如果对其用法以及掌握的老铁可以直接跳过,查看源码解析部分。)
1、添加依赖
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
2、定义网络服务接口
添加依赖并同步后,创建一个接口,专门负责定义网络API。这里我们采用接口是用于
调试Github的开放API,接口为:api.github.com/users/octoc… 在请求后会获取Github用户Octocat的Repo列表:
复制其数据,用AS的GsonFormat自动生成Javabean:
在这里需要注意这个接口的Json数据是以中括号{开头,是一个Json字符串数组,即对应的JavaBean是一个List,这里生成的Repo是这个List中元素对应的数据结构。对此我们可以定义获取此数据接口。
public interface NetService {
@GET ("users/{user}/repos") //配置Get请求、URL路径
Call<List<Repo>> getRepos(@Path("user") String user); //指定返回Call<T>对象,这里的T指定网络解析数据后返回的类型
//@Path("user")表示参数user会替换URL路径中的{user}
}
3、在AndroidManifest.xml中添加网络权限:
<uses-permission android:name="android.permission.INTERNET"/>
4、实现网络服务接口
创建好接口后,接下来就是用Retrofit实现此接口请求逻辑:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com") //配置URL的基地址
.addConverterFactory(GsonConverterFactory.*create*()) //配置Gson转换器
.build();
NetService netService = retrofit.create(NetService.class); //用Retrofit对象返回一个NetService的实现
Call<List<Repo>> octocat = netService.getRepos("octocat"); //获取Call对象,用该对象的enqueue实现异步请求
octocat.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
for (Repo rp : response.body()){
Log.i(TAG,"get the id:"+(rp.getId())); //获取数据后打印ID
}
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
Log.i(TAG,"onFailure:"+(t.toString()));
}
});
运行后可见打印的日志:
至此,完整的一次网络请求成功了。当然,Retrofit还可以进行更多的复杂操作,如配置请求头、请求提、表单提交等,更多方式可参考square.github.io/retrofit/。
源码流程图
下面是调用Retrofit做一次网络请求的代码流程:
动态代理
Retrofit源码中使用了动态代理,在看源码之前我们有必要先了解。在某些场景下,我们要用某些功能,但不是直接调用实现类,而是通过代理类来完成的。通过代理,我们可以隐藏实现类的细节,在不修改实现类的基础之上,增加额外的功能等。在日常生活中,代理模式处处可见,例如房屋中介、二手车贩子、代购等等。我们所说的代理,一般是指静态代理,即一个实现类对应一个代理类,彼此一一对应。假如你现在是个大型轮胎制造商,有家汽车公司准备在你这里订购车轮胎,我们用代码实现下:
首先,实现个接口,表示我们要做的事情——造轮胎:
public interface Wheel {
void produce();
}
然后实现这个接口:
public class WheelMaker implements Wheel{
@Override
public void produce() {
Log.i("proxyDemo","造好并装上车轮");
}
}
单独的车轮是没有用的,这个时候汽车厂商就来了,要把车轮装上去,汽车厂商实现代码如下:
public class BenZ implements Wheel{
private Wheel wheel;
public BenZ(Wheel wheel){
this.wheel = wheel;
}
@Override
public void produce() {
before();
wheel.produce();
after();
}
private void after() {
Log.i("proxyDemo","BenZ整车组装完毕,可以售卖了");
}
private void before() {
Log.i("proxyDemo","BenZ组装好其他零件,只缺装轮胎了");
}
}
汽车厂商来了,拿走了轮胎,就下来就是组装了,组装好就可以售卖,然后才有钱回款给你,你公司才能赚钱,代码如下:
Log.*i*("proxyDemo","--------------静态代理-------------");
Wheel wheel = new WheelMaker();
BenZ benZ = new BenZ(wheel);
benZ.produce();
汽车公司拿着你给的轮胎接口后进行组装,然后售卖,代码很简单没必要过多解释了。对应执行后对应输出如下:
随着日积月累,你越做越大,军方来找你做坦克的车轮。你咬咬牙,接了这笔订单。由于军方车轮规格要求更严,对应的,你重新安排了一条生产线接口,做特殊轮胎:
public class SpecialWheelMaker implements Wheel{
@Override
public void produce() {
Log.i("proxyDemo","造好特殊车轮并装上-");
}
}
听说你造好后,军方来人,拿走你的轮胎回去组装并给了你项目回款:
public class Tank implements Wheel{
private SpecialWheelMaker specialWheelMaker;
public Tank(SpecialWheelMaker specialWheelMaker){
this.specialWheelMaker = specialWheelMaker;
}
@Override
public void produce() {
before();
specialWheelMaker.produce();
after();
}
private void after() {
Log.i("proxyDemo","Tank组装完毕,准备上战场了");
}
private void before() {
Log.i("proxyDemo","Tank组装好其他零件,只缺装轮胎了");
}
}
组装完毕那天,军方邀请你去参观,于是你一边吩咐手下人继续做事,一边前去参观,代码如下:
Log.i("proxyDemo","--------------汽车的静态代理-------------");
Wheel wheel = new WheelMaker();
BenZ benZ = new BenZ(wheel);
benZ.produce();
Log.i("proxyDemo","--------------Tank的静态代理-------------");
SpecialWheelMaker specialWheelMaker = new SpecialWheelMaker();
Tank tank = new Tank(specialWheelMaker);
tank.produce();
对应输出为:
你很开心,因为随着这些项目的顺利落地,你的名声越来越大,无数汽车厂商找上门来合作,但你也发现,成本太高(代码臃肿)了。什么别摸我、玛傻拉弟弟轮胎都几乎一样,除了个别细节不一样外,如此为何还要给他们各个品牌之间独立的代理?22世纪了,早就没有中间商赚差价了,于是你自问自答,一套工业流程,能不能代理所有厂商制造代理呢?答案是肯定的。另外,从代码的角度上来看,如果新增一个厂商,就又要新加一个代理类,代码量会不停增加,而在代理类中的before()和after()都是重复的,不能复用。从某种方面来说,这就不属于好代码了。
这里就需要用到动态代理了。动态代理不需要事先创建代理(汽车厂商)类,而是根据需求动态创建。相当于一个工厂流水线,对应不同种类型轮胎产品,不论轮胎类型数量增加多少,生产线只有一个。从代码的角度来说,不会增加汽车厂商类,厂商的公共方法就能得到复用。
首先,我们定义一个动态代理类,需要实现InvocationHandler接口,然后在invoke()中添加相应的逻辑:
public class DynamicProxyWheel implements InvocationHandler {
private Object wheel; //代理的对象
public DynamicProxyWheel(Object wheel){
this.wheel = wheel;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
method.invoke(wheel,args);
after();
return null;
}
private void after() {
Log.i("proxyDemo","全部组装完毕,准备交付");
}
private void before() {
Log.i("proxyDemo","先组装好其他部件及系统");
}
}
接下来,我们使用这个动态代理轮胎来生产各种轮胎了:
public class DynamicProxyWheel implements InvocationHandler {
private Object wheel; //代理的对象
public DynamicProxyWheel(Object wheel){
this.wheel = wheel;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
method.invoke(wheel,args);
after();
return null;
}
private void after() {
Log.i("proxyDemo","全部组装完毕,准备交付");
}
private void before() {
Log.i("proxyDemo","先组装好其他部件及系统");
}
}
可见,这里少了BenZ和Tank这些厂商(代理)类。这就是动态代理的使用,而Retrofit就是通过动态代理的方式创建各种网络接口的代理。
Builder模式
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com") //配置URL的基地址
.addConverterFactory(GsonConverterFactory.*create*()) //配置Gson转换器
.build();
在声明Retrofit对象的时候,我们可以看到这里应用了一个建造者(构建者)模式,建造者模式的特点是可以讲一个复杂对象的构成和表示分离开来。这里,我们主要关注build()方法的实现:
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//设置Call的工厂类,如果没有设置,则为OkHttpClient对象
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
//执行回调方法的对象,Android里是在主线程执行回调
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
//网络请求适配器工厂集合
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
//数据转换器工厂类集合,用于解析网络响应
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories =
new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
return new Retrofit(
callFactory,
baseUrl,
*unmodifiableList*(converterFactories),
*unmodifiableList*(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
如果这时候是一头雾水,很正常,我们先尝试去理解代码,到最后,我们都会看明白的。 在Build()中,初始化了回调Call的工厂类,网络请求适配器工厂集合,数据转换器工厂集合。可见其中Call的工厂类默认实现为OkHttpClient。默认的CallAdapter.Factory为ExecutorCallAdapterFactory对象。CallAdapter.Factory主要适配接口的返回类型,如我们上述例子的接口:
public interface NetService {
@GET ("users/{user}/repos")
Call<List<Repo>> getRepos(@Path("user") String user);
}
返回为Call类型,而Call类型则是由ExecutorCallAdapterFactory适配的,如果在创建Retrofit对象的时候指定配置RxJavaCallAdapterFactory:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
则接口getRepos的返回类型可为RxJava的Observable。
Build()中还初始化了数据转换器工厂类集合,转换器工厂主要负责网络响应的解析,比如我们之前代码中是:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.*create*())
.build();
这里设置了GsonConverterFactory,我们就可以使用Gson解析网络结果,当然还有其他的工厂,比如PhotoConverterFactory,可以使用PhotoBuf解析网络结果。
build()中值得注意的一行代码是:
这里限定了默认的网络回调器,通过绑定主线程Looper的Handler强制将网络回调切换到主线程中执行。相比OkHttp3,Retrofit在使用时一个很方便地方就是在execute() 或者 enqueue() 发起请求后的返回结果时,不需要再切换线程,因为此刻,它已经在安卓的UI主线程当中了。
这里我们可以总结下这几个成员变量:
serviceMethodCache:这个map的第一个泛型参数Method即是我们的请求方法。S erviceMethod主要代表网络请求接口中方法进行注解之后我们通过解析解析后拿到的对象,与注解中的post、get等方法成对出现,一一对应。serviceMethodCache看名字就知道是缓存,在这里主要是做网络请求相关配置的缓存(之前2.3版本的时候是个LinkedHashMap)。
call Factory:请求网络的OKHttp的工厂,用于“生产”OKHttp的OKHttpClient;
baseUrl:网络请求的url的基地址,与接口参数拼接起来就是个完整的URL;
converterFactories:数据转换器工厂集合,用于生产我们需要的数据转换器(数据转换器:对我们做了网络请求后得到的response进行转换成我们设定的Java对象);
callAdapterFactories:网络请求适配器工厂集合,用于放置我们的网络请求适配器工厂(网络请求适配器:把我们的call对象转换成其他类型);
callbackExecutor:用于执行回调,Android类中默认的网络回调执行器为Main ThreadExecutor,通过绑定主线程Looper的Handler将网络回调推送到主线程执行。(Retrofit当中的网络请求,最终都是通过线程池将我们的handler来进行调配,可以处理我们的主线程和子线程等线程切换)毫无意外,我们处理异步的网络请求就需要用到它;
validateEagerly:一个标记位,表示是否需要立即解析我们接口的方法。
我们继续看Builder(),在这里Builder是Retrofit的静态内部类:
同样也有几个成员变量,有两个新参数要了解下:
Platform:表示Retrofit的适配平台(Android,Java8等);
baseUrl:网络请求的URL地址(注意这里是HttpUrl,不是String);
converterFactories,callAdapterFactories,callbackExecutor,validateEagerly作用同上。
这里的构造方法中返回的是Platform.get(),即适配平台:
baseUrl()
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.*create*())
.build();
在原例子中,第二行就指定baseUrl(),其方法内部主要工作就是对传入的String进行判空和URL转换:
注意这里return的是转换好后的HttpUrl类型。
这里的baseUrl()将原String拆分成多个字符碎片,然后检测最后一个字符是否是以“/”结尾,如果不是则抛出异常:baseUrl不是以“/”结尾。如果是的话才return。
addConverterFactory
addConverterFactory()操作比较简单,添加工厂集合:
那么重点就是里面的参数了:
可见,最后传的还是Gson转换器工厂对象。
addCallAdapterFactory
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.*create*())
.addCallAdapterFactory(RxJavaCallAdapterFactory.*create*())
.build();
网络请求适配器工厂,所做操作与addConverterFactory()一样,也都是一样的添加操作。
我们看RxJavaCallAdapterFactory.create()的实现:
这个Scheduler就是RxJava中的调度器。也就是说这里的.create()返回了含有Scheduler调度器对象的RxJavaCallAdapterFactory,然后添加到callAdapterFactories集合中。
对象构成
这时候再返回我们最初的build():
相信到这里,你已经不再是一头雾水了。一句话概括这段代码的含义:将retrofit类中的所有成员变量配置完毕,完成整个Retrofit对象的构建。
网络请求
在构建好Retrofit对象后,我们就要对其进行网络请求了:
在这里,create()用Retrofit对象返回一个NetService的实现,我们观察下其内部实现:
validateServiceInterface()方法名字直译就是否是有效服务接口,方法内部都是对其是否合法接口的一个判断,validateEagerly一个标记位,表示是否需要立即解析我们接口的方法。如果是,就立即解析。这里我们重点看platform.isDefaultMethod()
判断有没有Java8的TYPES,且是默认方法。即判断是不是Java8的默认方法。
这个方法即判断是否是静态方法。
Java接口默认不许有默认实现,但是Java8开始可以给接口的一些方法写默认实现了;Java接口不允许写静态方法,但Java8开始允许接口里写静态方法了。然而Retrofit是不支持的这些的,即Retrofit不接纳这两种方式为service接口里的方法。
所以这里判断就是要求不是JAVA8的默认方法和静态方法。这时候才到重点的方法来:
也就是说validateServiceInterface()做了一系列接口的合规性验证后,最终执行loadServiceMethod()。
在进这个方法之前,我们先跳回原外界方法:
下面的代码可以看出来,是一个动态代理。我们观察其主要逻辑,第一个判断,方法对象是否是Object(对象),如果是则直接调用不代理。接着来到return后的判断方法:
如果platform.isDefaultMethod(method),则返回platform.invokeDefaultMethod(method, service, proxy, args),如果不是,则loadServiceMethod(method).invoke(args)。
为了便于理解,上面的三元判断方法等价于:
if(platform.isDefaultMethod(method)){
platform.invokeDefaultMethod(method, service, proxy, args)
}else{
loadServiceMethod(method).invoke(args);
}
还是判断是否是默认方法,如果不是,才执行loadServiceMethod(method)。结合上面代码,可看出无论validateServiceInterface(),最后都会执行loadServiceMethod(),可见loadServiceMethod()才是关键中的关键。
loadServiceMethod()
这里的synchronized线程同步锁,保证我们的线程安全。
serviceMethod,在这里对应的是接口方法(的封装)。serviceMethodCache在这里是网络请求相关配置的缓存,本质是一个Map,在这里这个Map的key是method,如果能将对应method的value即serviceMethod取出,且不为空则return出去。如果为空,核心代码又变成了:
在这里,又去尝试获取serviceMethodCache中的serviceMethod。如果没有得到,即为空的情况下执行ServiceMethod.parseAnnotations():
先看①:
可以看到这个建造者模式创建了一个完整的method对象(包含了网络请求的所有参数method,baseUrl,httpMethod,headers,contentType,hasBody,isFormEncoded,isMultipart,isKotlinSuspendFunction等,这里不细讲了。)
再看②:
代码有点多…精简下:
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;//是否是Kotlin suspend方法
...
Annotation[] annotations = method.getAnnotations();//获取method的注解信息
...
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
这个类中有几个参数要注意一下:
callFactory是网络请求工厂,用于生产我们的网络请求Call,即OKHttp的call。代表着实际的网络请求。
CallAdapte r表示的是网络请求适配器,主要的把我们的Call请求适配不同的平台,比如RxJava的平台。
responseConverter表示的是数据转换器,其实就是reponse内容转换器,作用把服务器返回的Json数据转换成我们的JavaBean对象。
这里的httpMethod主要表示网络请求的HTTP方法,比如GET,POST等等。
annotations即网络请求方法中的注解,即原我们的代码接口中的@GET等:
parameterTypes即获取我们网络请求接口方法里的类型。 我们回来看其初始化的地方:
看到这里,遍历工厂集合,然后通过get()来获得我们需要的CallAdapter。如果没有合适的,就抛出异常。拿到CallAdapter之后,拿到其responseType(返回的接口类型),这个时候我们就根据我们网络请求方法的返回值、注解类型,从我们的retrofit对象中获取这个网络数据适配器返回的数据类型。
在这里,我们通过createResponseConverter获取到数据转换器类型。看下这个方法的实现:
这里再通过获取到的注解annotation和之前获取到的responseType再从retrofit中获取:
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
Objects.requireNonNull(type, "type == null");
Objects.requireNonNull(annotations, "annotations == null");
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
StringBuilder builder =
new StringBuilder("Could not locate ResponseBody converter for ")
.append(type)
.append(".\n");
if (skipPast != null) {
builder.append(" Skipped:");
for (int i = 0; i < start; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
builder.append('\n');
}
builder.append(" Tried:");
for (int i = start, count = converterFactories.size(); i < count; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
throw new IllegalArgumentException(builder.toString());
}
源码逻辑流程很像之前的CallAdapter,这里也是遍历converterFactories并从中获取合适的数据转换器工厂,然后再通过responseBodyConverter()获取到相应的数据转换器。(默认是Json转换器,所以这里一般获取到的是JsonResponseConverter,了解即可)这样就完成了整个数据转换器的初始化工作。
从接下来的代码中可以看出,if (!isKotlinSuspendFunction)时,即在Java下开发的非协程挂起函数,直接返回其子类CallAdapted<>对象。
这里先暂停,我们先看其invoke()(HttpServiceMethod extends ServiceMethod,而ServiceMethod中有抽象方法invoke()):
这个adapt是抽象方法,那具体的实现呢?就在上面CallAdapted中(extends HttpServiceMethod,要复写其抽象方法adapt()),其核心代码就一行:
在说adapt()之前先说说OkHttpCall:
OkHttpCall
这个OkHttpCall其实就是对OkHttp中的Call的一个封装。
同时其构造方法也把callFactory、responseConverter等参数传入。
其自身也封装了异步、执行等方法,所以retrofit的网络请求到底还是调用的OKHttp库。
adapt
我们上面说到,把创建好的OkHttpCall对象传进了callAdapter的adapt()之中,并返回。
CallAdapter是个接口,本身没有方法实现逻辑。由于这里是动态代理,因此我们要到各实现类中去寻找此方法具体实现,例如RxJavaCallAdapter等:
其主要作用就是把我们的一个一个的Retrofit当中的Call转换成其他平台也可以使用的类型。比如在RxJavaCallAdapter中就转换成了Observable,具体转换方式这里就不细说了。
总结下,先回到我们的网络请求代码:
这里的NetService是个接口,接口肯定不能直接调用方法,所以是在create()中通过动态代理Proxy.newProxyInstance去进行拦截,然后调用其InvocationHandler中的invoke()来进行实际的操作,然后通过返回的OkHttpCall对象来进行实际的网络请求。
所以这个netService实际上就是通过动态代理返还过来的OkHttpCall对象。而OkHttpCall对象又是对OkHttp的封装,所以这里的.getRepos(“octocat)其实就是通过我们的OkHttp库去请求我们网络然后实行同步和异步的方法。
Retrofit的请求,OKHttp的创建
Retrofit的请求其实也分为两种:
1、同步:OkHttpCall.execute();
2、异步:OkHttpCall.enqueue();
(这里的OkHttpCall是指的create()里返回的OkHttpCall对象)
这里我们先关注我们代码中的异步方法:
可见,这个call是个接口,其所对应的实现类是OkHttpCall。
在这里获取call的方式要关注这个方法:
可以看到,call通过callFactory创建,在原OKHTTP代码中,如果要创建一个call,则代码是这样的:
okHttpClient.newCall(request).enqueue(new Callback());
newCall()中参数是一个OkHttp里的Request对象,Request对象在Retrofit中通过requestFactory.create()创建,创建好后传给callFactory的newCall()并生成okhttp3.call对象。callFactory对象的创建在HttpServiceMethod的parseAnnotations()方法中,上面已经说明,这里不再赘述。
其对应回调也在OkHttpCall中,这时不知道为什么,感觉有所遗漏,我们找到HttpServiceMethod的parseAnnotations()中的callFactory;
原来很早之前,在build()里就说明了Retrofit用来创建OkHttp3的Call的工厂就是OkHttp3的OkHttpClient()。同理,execute()的创建也是类似,这里就不赘述了。此流程一走下来,OkHttp对象创建完毕。
数据解析与回调
在call的异步方法里,在得到初始的rawResponse后,有一个parseResponse()的操作:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
// Remove the body's source (the only stateful object) so we can pass the response along.
rawResponse =
rawResponse
.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
try {
// Buffer the entire body to avoid future I/O.
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
不难看出,在这里,retorfit将OkHttp返回的数据进行解析,然后将返回数据转换后再return,重点在这里:
这个responseConverter转换器的功能,就是将OKHttp返回的不易看懂的数据转换为我们自定义的JavaBean,在之前HttpServiceMethod.parseAnnotations()中:
前文有讲述,最终调用的地方是
遍历converterFactories并从中获取合适的数据转换器工厂,然后再通过responseBodyConverter()获取到相应的数据转换器。(默认是Json转换器,所以这里一般获取到的是JsonResponseConverter,了解即可)我们可以看出responseConverter来自于converterFactories,而在Retrofit的build()中:
而这个this.converterFactories赋值的地方在:
即我们写的代码中的:
也就是说这个最早初始化的Gson转化器最终被OkHttpCall的parseResponse()中的responseConverter.convert(catchingBody);调用。
这里解析完毕后,会执行onResponse回调,将OkHttpCall对象和解析数据后的response对象回调出去,对应的我们的代码中的:
设计模式
经过上述的源码流程梳理,不难发现Retrofit其实本质上就是一个网络请求框架的封装。实际上的网络请求等功能实现都是交给了OkHttp去完成。在这些代码封装之中,融合了太多的设计模式在里面。
建造者模式
这里很明显有个构建者模式,构建者模式将一个复杂对象的构造与它的表示分离,使得建造过程可以创建不同的表示 。 其优点很明显,这里构建者模式加上链式调用为Retrofit的参数配置增加不少灵活度,进一步增强代码可读性。
外观模式 ( 门面模式 )
门面模式要求一个子系统的外部与其内部通信必须通过一个统一的对象进行。Retrofit给我们暴露的方法和类不多。核心类就是Retrofit,我们只管配置Retrofit,然后获取接口对象请求数据,设置回调,其他的都是Retrofit框架内部的事情了。这里Retrofit的门面是Retrofit.create() 。这样的代码设计能降低系统耦合度,除了Retrofit,平时开发常用的开源框架Glide也有一个门面,比如Glide.with(xxx)… 。
动态代理
动态代理指的是程序运行的时候创建代理类的方式,代理模式中最重要的就是区分角色: 1、目标接口;2、目标对象;3、代理对象。
Retrofit中则是通过Proxy.newProxyInstance去调用其InvocationHandler中的invoke()来实现动态代理,详情上面已经说明,这里不再赘述。
装饰模式
装饰模式是在不改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。Retrofit中用装饰模式的就是ExecutorCallbackCall了。
简单说下,enqueue()方法是异步的,当你调用OkHttpCall的enqueue方法,回调的callback在子线程中,如果需要在主线程接受回调,那就要通过Handler转换到主线程上去。ExecutorCallbackCall就是用来干这个事。当然以上是retrofit默认使用的切换线程方式。如果我们指定用rxjava,那就不会用到这个ExecutorCallbackCall而是RxJava的Call了。
也许你会感觉,装饰模式与静态代理模式很像。但这俩者区别也不小:装饰模式里,装饰后的对象还是“我”,只不过装饰完后“我”的功能更加强大;代理模式里对象已经不是“我”了,只不过代理模式可以联系到我而已。
策略模式
策略模式定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换。在CallAdapter中可以添加多个CallAdapter.Factory对象,相当于我们封装了多个不同的适配算法CallAdapter.adapt()。上文已经说明,其对象的生成初始化和调用都在HttpServerMethod.parseAnnotations()中:
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
......
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
......
}
......
}
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
try {
//noinspection unchecked
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) {
.......
}
}
然后方法执行到Retrofit对象的callAdapter() -> nextCallAdapter()。
在的nextCallAdapter()中,根据返回值retrunType类型遍历调用CallAdapter.Factory的get()方法:
如果返回的CallAdapter对象不为null则直接返回该对象,此逻辑即印证了CallAdapter.Factory的get()应该是根据retrunType确定是否返回CallAdapter对象(是否选择该种策略)。
适配器模式
适配器模式将一个类的接口变换为客户端期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。Retrofit代码中将大量的设计模式融合在一起,除了策略模式,CallAdapter中还有此模式,
这段注释似乎就是在说明,将响应类型为{@code R}的{@link Call}修改为{@code T}类型。Retrofit中,CallAdapter就采用了适配器模式为创建访问Call接口提供服务。默认情况下使用默认的 ExecutorCallAdapterFactory 将okhttp3.call转变成为 retroift中的call,如果设定RxJava,则将okhttp3.call转化为 Observable。上述源码有分析,此处不赘述。
如果你对上述中所描述的知识点还不是很清楚的话,推荐你看下 《OKhttp 源码解析》,里面记录的知识点比较详细,有需要的可以 点击这里直接获取!!!里面记录许多Android 相关学习知识点。↓↓↓
Android 技术提升知识点归整
Android 性能调优系列:https://qr18.cn/FVlo89
Android 车载学习指南:https://qr18.cn/F05ZCM
Android Framework核心知识点笔记:https://qr18.cn/AQpN4J
Android 音视频学习笔记:https://qr18.cn/Ei3VPD
Jetpack全家桶(含Compose):https://qr18.cn/A0gajp
Kotlin 入门到精进:https://qr18.cn/CdjtAF
Flutter 基础到进阶实战:https://qr18.cn/DIvKma
Android 八大知识体系:https://qr18.cn/CyxarU
Android 中高级面试题锦:https://qr18.cn/CKV8OZ
后续如有新知识点,将会持续更新,尽请期待……