本文主要讲述Retrofit的源码分析,结合个人总结的结构图,配上Retrofit源码,可以很大程度地帮助理解Retrofit的内部结构
2 Retrofit结构预览
此图是本人耗费一周时间阅读源码整理出来的结构图,点此获取pdf文件与结构文件
关于Retrofit2的使用,网上有大量的文章做了讲解,本文便不再进行重复阐述,重点来讲述Retrofit2的结构流程,废话不多说
3.1 Retrofit对象实例化下面是使用retrofit进行一个简单的网络请求
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.xxxxx.com")
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.build();
OKHttpService httpService = retrofit.create(OKHttpService.class);
httpService.login("ssss", "123123").enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
Log.i("WeiSir", "陈宫: " + Thread.currentThread());
}
@Override
public void onFailure(Call call, Throwable t) {
Log.i("WeiSir", "失败: " + Thread.currentThread());
}
通过构建者模式创建Retrofit对象,依次来看每一行的功能
- Retrofit.Builder:在Retrofit的Builder静态类中,主要执行了Platform.get()方法,其内部的findplatform()判断当前的执行平台,因为这里是在Android平台中运行的,所以会return new Android()返回一个Platform对象,之后在网络请求结束后,默认会通过内部的主线程handler的post()将异步线程切换到主线程在结构图中有对应的源码)
- baseUrl():关于baseUrl在图中没有标注,其主要功能是将传入的地址转换为HttpUrl,return baseUrl(httpUrl)中会获取网址的path部分,并将这部分每一级路径分割成为一个集合,来判断路径的的最后结尾是否是’/‘结束,所以在传递基地址时path路径的结尾必须要以’/‘结尾,否则会抛IllegalArgumentException(""baseUrl must end in /: "+baseUrl)异常,有人会说,“以前没加怎么没碰到异常呢?”,注意,该方法判断的是传入地址的路径部分,如果地址为’https:www.baidu.com’,这里只有协议类型和域名地址,没有路径部分,所以不写’/'是不会报错的
- addConverterFactory():添加一个数据转换工厂,若想使login()返回值是自己Call对象,就需要用到数据转换工厂,此工厂默认添加的是BuiltInConverters,返回原始的Call< ResponseBody>,这里添加的所有数据转换工厂最终都会被加入到List
集合中,并不会进行覆盖 - build() 熟悉构建者模式的都知道,前面的操作仅仅只是将参数保存起来了,真正的对象创建是在build()中实现的,其中添加了默认的默认的网络执行器,回调方法执行器(这里的执行器默认就是Builder对象中Platform.get()下获取的MainThreadExecutor),网络请求适配器,数据转换器,结合图片来看
public Retrofit build() {
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) { //添加默认的request执行工厂
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) { //创建网络回调执行器
callbackExecutor = platform.defaultCallbackExecutor();
}
List callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));//添加网络请求适配器
List converterFactories = new ArrayList<>(1 + this.converterFactories.size());
//添加默认的数据转换工厂
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
}
3.2 自定义网络请求接口定义
public interface OKHttpService{
@FormUrlEncoded
@POST("user/login")
Call login(@Field("username") String userName, @Field("password") String password);
}
这里没什么好说的,若想使用自定义的javabean对象,将ResponseBody改成相应的javaBean对象即可
3.3 创建网络接口代理类(核心)OKHttpService httpService = retrofit.create(OKHttpService.class);
create()内部实现了Retrofit的核心功能的创建和调度
publicT create(final Class service) { //.....省略... return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class>[] { service }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args) //这里看来原方法不一定会被调用,主要执行代理的操作 if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } //核心的三行代码 ServiceMethod
按照图中第2 步,执行create方法,内部是return一个动态代理的OKHttpService代理对象
3.4 动态代理Proxy.newProxyInstance()动态代理模式:在程序运行期间,通过反射机制,动态创建OkhttpService.class的代理对象,并且对该对象中的方法增加一些其他操作
我个人认为Square公司当初在选择使用动态代理模式,应该就是借用了此方式能自动为接口创建一个空实现,而他主要关心的并不是原方法调用,而是在invoke()中执行关键的增强操作,免去为每个网络请求接口的实例化去创建源代码
Proxy.newProxyInstance(ClassLoader,Class>[] interfaces,InvocationHandler)
- ClassLoader 接口OKHttpService的ClassLoader对象,在反射机制中创建实例需要使用
- Class 需要生成代理类的原接口
- InvocationHandler 在调用该代理类对象的login()方法时,会回调h中的invoke()方法,代理操作便是在这里添加
//下为newProxyInstance关键代码
final Class>[] intfs = interfaces.clone(); //若interface没有重写clone(),默认的是进行一次浅拷贝,即实例对象相同,引用不同
Class> cl = getProxyClass0(loader, intfs);//生成或获取(缓存中)代理类的Class对象
final Constructor> cons = cl.getConstructor(constructorParams); //通过Class对象获取构造方法
return cons.newInstance(new Object[]{h}); //通过构造方法创建实例对象
对于动态代理类的newProxyInstance()内部,主要逻辑就这么几行,要注意的是getProxyClass0(),获取的Class对象是一个单例模式,在第一次时会创建实例并将其存入WeakCache做缓存,之后的获取会直接从缓存中获取Class对象,至于其他一些操作,多为反射机制实例化对象常用的调用
3.5 Proxy代理回调接口InvocationHandler对于InvocationHandler读者只需记住内部的invoke()被调用是在开发者调用login()方法,也就是通过newProxyInstance()返回的代理类对象(OKHttpService的代理实现类)的login()时,会触发invoke(),而实现代理功能,也是在内部的method.invoke()前后做代理增加功能
public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
//.....做一些扩展操作
method.invoke(this, args); //这一行是真正的原对象方法的调用
//.....包括下边
}
执行httpService.login()时,invoke()就被执行了
以上为Proxy.newProxyInstance()中的简略源码,我们继续回到create()源码中,return代理对象,至此OKHttpService接口的代理对象就被创建了,接下来就要使用代理对象httpservice调用login()方法了
httpService.login("19834431149", "123123")
上面已经说过,开发者调用login()方法,会执行InvocationHandler中的invoke(),那么此方法具体实现:
public Object invoke(Object proxy, Method method, @Nullable Object[] args){
//......核心代码三行
ServiceMethod serviceMethod =
(ServiceMethod) loadServiceMethod(method);//加载ServiceMethod对象
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); //创建OkHttpCall
return serviceMethod.adapt(okHttpCall);//将servicemethod内部的网络请求适配器与call适配
}
3.5.1 ServiceMethod对象实例化
一个ServiceMethod对象对应了一个网络接口中的方法,该对象中保存有方法的处理网络请求所要用到的各种参数及方法的属性注解等,阅读源码时要搞清楚serviceMethod的性质,参照结构图,调用loadServiceMethod()并入方法内部
ServiceMethod, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
同样,首先会从缓存中获取对象,为null才会创建实例对象,也是一个单例对象,在Builer()中会将解析method一些信息(参数,注解,参数注解)和retrofit对象中的属性,并保存到serviceMethod对象中
在build() 方法中操作,结构图列了三个关键的执行代码
callAdapter = createCallAdapter(); responseType = callAdapter.responseType(); responseConverter = createResponseConverter();
//我们分别看一下每一个执行的具体操作是些什么
3.5.1.1 createCallAdapter()用于从网络请求适配器工厂中获取一个网络请求适配器
{内部逻辑
Type returnType = method.getGenericReturnType();//获取方法的返回值类型
Annotation[] annotations = method.getAnnotations(); //获取方法的注解
return (CallAdapter) retrofit.callAdapter(returnType, annotations);
}
进入到retrofit.callAdapter()内部的nextCallAdapter()中
{
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
//callAdapterFactories为创建retrofit.build()时添加的网络请求适配器工厂集合,在loadServiceMethod()时赋值到ServiceMethod对象中,如果想不起来则回到loadServiceMethod的Builder()或者到retrofit的build()中看看,加深印象
CallAdapter, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
//get(i)获取网络请求适配器工厂,在集合中默认添加的是ExecutorCallAdapterFactory(),当然通过retrofit的addCallAdapterFactory()是可以添加多个适配器工厂的
//get(returnType, annotations, this)根据返回值类型,注解,获取工厂中的网络请求适配器
}
举ExecutorCallAdapterFactory为例子:get(i)获取到此对象后,调用内部的get(returnType, annotations,retrofit)方法,
//get()内部 如果是使用的rxjava ,执行get方法时,返回的对象中线程切换,内部也是通过adapt();来实现的
class ExecutorCallAdapterFactory{
CallAdapter,?> get(...){
return new CallAdapter>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call adapt(Call call) { //该方法会在之后进行调用
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
}
3.5.1.2 callAdapter.responseType()
此操作是调用上面的responseType()用以check返回值类型的定义
3.5.1.3 createResponseConverter()用于获取一个响应数据转换器,还是继续点进去,执行retrofit.responseBodyConverter(responseType, annotations);
再进去直到进入nextResponseBodyConverter()中
ConverternextResponseBodyConverter(){ for (int i = start, count = converterFactories.size(); i < count; i++) { Converter converter = converterFactories.get(i).responseBodyConverter(type, annotations, this); if (converter != null) { //noinspection unchecked return (Converter ) converter; }
可以发现这里做的工作其实和createCallAdapter()是类似的,默认的数据转换工厂为BuiltInConverters,此类内部两个方法requestBodyConverter()和responseBodyConverter这里调用responseBodyConverter(),返回默认的返回值类型,也就是Converter
在build()不仅仅包含有这些操作,我们简单来看看,帮助理解ServiceMethod的性质
parseMethodAnnotation(annotation); //检查login()方法的注解 parameterHandlers = new ParameterHandler>[parameterCount]; //保存参数列表,并且做检查 Annotation[] parameterAnnotations = parameterAnnotationsArray[p];//保存参数注解,然后做检查 剩余的一堆if条件用来判断比如请求头,body,以及在添加一些参数注解过程中,有没有配合方法注解来使用呀等等的,
通过这里可以认识ServiceMethod不仅存储了method(也就是login()方法)的参数,属性等,还存储有各种适配器,转换器,和http协议要求的一些相关check,需要结合http相关的知识来理解
结合结构图,回到invoke方法第二行逻辑
3.5.2 OkHttpCall实例该类比较容易看懂,创建OkHttpCall对象,实现Call接口,重写了execute()和enqueue方法等,在随后的网络请求适配器调用中,就会执行此方法来通过OkHttp请求访问网络
最后一行
将创建的okHttpCall对象与serviceMethod中的网络请求适配器适配,内部return callAdapter.adapt(call);而此callAdapter还记得在哪获取的?
它是在serviceMethod.loadServiceMethod(method)内的createCallAdapter()中选择的,所以这里的引用是之前选择的网络请求适配器类对象的adapt(),我们就先看一下它默认的网络请求适配器也就是ExecutorCallAdapterFactory
@Override public Calladapt(Call call) { return new ExecutorCallbackCall<>(callbackExecutor, call); //返回一个默认的ExecutorCallbackCall }
当执行了serviceMethod.adapt(okHttpCall)时,其实(默认)调用了此处的adapt()方法,返回一个call对象
3.6 login().enqueue()执行网络请求ok,到这里来回忆一下,当客户端调用login()方法时,是使用的Proxy.newProxyInstance()返回的代理实例对象httpService去调用的,并且会回调InvocationHandler中的invoke()方法,执行代理操作,最后通过adapt()返回一个call对象(主要还是结合结构图来理解这一流程)
上面我们讲过login()方法会返回一个Call对象,此对象重写了enqueue(),execute()等处理请求的方法,那么此处enqueue()执行的便是此对象中enqueue(),我们来简单看一下默认的ExecutorCallbackCall<>(callbackExecutor, call)中对enqueue()是如何操作的
public void enqueue(final Callbackcallback) { //delegate是在实例化ExecutorCallbackCall时传递的第二个参数,对象为OkHttpCall //也就是说这里enqueue()又委托给了OkHttpCall来执行 delegate.enqueue(new Callback () { @Override public void onResponse(Call call, final Response response) { //在Android环境中运行callbackExecutor默认为MainThreadExecutor,图中有相关简介 //当网络请求成功,执行切回到主线程中,回调客户端的callback对象的onResponse()方法 callbackExecutor.execute(new Runnable() { @Override public void run() { if (delegate.isCanceled()) { callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled")); } else { callback.onResponse(ExecutorCallbackCall.this, response); } } }); } //失败同样也会切换到主线程去回调callback的onFailure() @Override public void onFailure(Call call, final Throwable t) { callbackExecutor.execute(new Runnable() { @Override public void run() { callback.onFailure(ExecutorCallbackCall.this, t); } }); } }); }
通过delegate.enqueue(new Callback
@Override public void enqueue(final Callbackcallback) { // ...省略... call = rawCall = createRawCall(); call.enqueue(new okhttp3.Callback() {.... //...稍后再看... }
里面是执行了一个createRawCall()方法,其中又执行了okhttp3.Call call = serviceMethod.toCall(args);再进入该方法内部
//主要的两行代码 RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,contentType, hasBody, isFormEncoded, isMultipart); return callFactory.newCall(requestBuilder.build());
这里首先根据serviceMethod中关于login()方法的属性以及解析的注解信息创建request请求对象,最后通过
return callFactory.newCall(requestBuilder.build());创callFactory默认是OkHttpClient,我们就不在深入展示了,最终在其内部返回的是一个RealCall,有兴趣的可以进去了解一下OkHttpClient源码,到了这里,Retrofit的网络请求底层便显露在我们眼前,从这里我们就可以看到,Retrofit使用的网络底层为OkHttp
接下来退回到`enqueue()方法体内,之后调用call(RealCall)对象的enqueue()来处理网络请求了,至于之后的事情,就是OkHttp底层要做的了.
接近尾声了,最后我们再来看一下使用RealCall调用enqueue()传入的Callback对象
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response response;
try {
//访问成功后,会将响应body转换为用户要求的格式
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
try {
//最后回调上层的callback对象方法
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
//至于剩余部分,则是访问失败回调上层接口,比较简单,不在概述
3.6.1.1 parseResponse(rawResponse)
最后在看一下如何进行的数据转换
ResponseparseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body();//获取body响应体 int code = rawResponse.code(); if (code < 200 || code >= 300) {.....} //如果响应码不在200之内,则为请求失败回调failure() if (code == 204 || code == 205) { Response.success(null, rawResponse) //返回空响应体的成功回调 }//凡是200以内的都已经访问成功 T body = serviceMethod.toResponse(catchingBody);//关键在这一行 //responseConverter.convert(body)内部执行了这样一行代码,使用响应体转换工厂来调用convert()进行数据转换
这里举一个GsonResponseBodyConverter(GsonConverterFactory创建)的例子把
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
T result = adapter.read(jsonReader); //就是使用adapter将响应体的二进制流重新转换为对应的返回值类型,具体是什么类型,在serviceMethod中已经确定好并且对其赋过值了
if (jsonReader.peek() != JsonToken.END_document) {
throw new JsonIOException("JSON document was not fully consumed.");
}
return result;
...
}
到此,delegate.enqueue()也差不多结束了,成功回调onResponse,失败回调onFailure(),最后客户端就可以看到网络的访问状况了
4 总结附录本来只是想简单介绍一下结构图的,但是越写越繁多,导致整篇文章下来篇幅变得更长了,不过最终也是希望看过本篇文章的尤其是看结构图后能对Retrofit框架有一个结构性的认识,也是值得的,最后,如果想要获取结构图pdf文件,在评论区留言把!!
HTTP状态码表



