综述

  Retrofit2的用法在Retrofit2.0使用详解这篇文章中已经详细介绍过了。那么在这就来看一下Retrofit2它是如何实现的。Retrofit2中它的内部网络请求是依赖于OKHttp,所以Retrofit2可以看做是对OKHttp的一次封装,那么下面就开看下Retrofit2是如何对OKHttp进行封装的。

回顾Retrofit2的使用

  在这里首先来回顾一下Retrofit2的使用。对于Retrofit2的使用可以分为三步。
  首先,我们创建一个Java接口GitHubService来作为Http请求接口。

public interface GitHubService {@GET("repos/{owner}/{repo}/contributors")Call<ResponseBody> contributorsBySimpleGetCall(@Path("owner") String owner,
@Path("repo") String repo);
}

  然后我们需要创建一个Retrofit实例,并通过Retrofit对象创建一个GitHubService接口的实现。

Retrofit retrofit = new Retrofit.Builder()//添加对RxJava的支持.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加对Json转换器的支持.addConverterFactory(GsonConverterFactory.create()).baseUrl("https://api.github.com/").build();
GitHubService service = retrofit.create(GitHubService.class);

  最后通过调用我们创建的接口便能够向后台发起请求。

Call<ResponseBody> call = service.contributorsBySimpleGetCall("square", "retrofit");
call.enqueue(new Callback<ResponseBody>() {@Overridepublic void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {......}@Overridepublic void onFailure(Call<ResponseBody> call, Throwable t) {......}
});

Retrofit2源码分析

  首先通过@GET来标识这个接口是一个GET请求。那么看一下这个GET注解的定义。

package retrofit2.http;import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import okhttp3.HttpUrl;import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {String value() default "";
}

  从注解中可以看出这个注解是对方法的声明,并且在运行时VM依然保留注解。Java自定义注解在这里就不在过多说明,可以参考 Java注解在Android中使用这篇文章。
  下面就再来看一下是如何创建Retrofit对象的。对于Retrofit对象的创建采用的是Builder模式。那么在这里我们就来看一下这个Builder类。

public static final class Builder {//对平台的支持private Platform platform;//发起请求OKHttp3的Client工厂private okhttp3.Call.Factory callFactory;//OKHttp2的HttpUrl对象,也就是将我们传入的baseUrl字符串包装成HttpUrlprivate HttpUrl baseUrl;//转换器工厂集合,retrofit可以插入多个转化器,例如:Gson,Jackson等等private List<Converter.Factory> converterFactories = new ArrayList<>();//用于发起请求和接收响应的Call适配器工厂集合,//Retrofit对RxJava的支持就是通过在该集合中添加RxJavaCallAdapterFactory,//而RxJavaCallAdapterFactory正是继承自CallAdapter.Factoryprivate List<CallAdapter.Factory> adapterFactories = new ArrayList<>();//Executor并发框架,用于对请求后响应结果的回调执行private Executor callbackExecutor;//是否需要立即生效private boolean validateEagerly;Builder(Platform platform) {this.platform = platform;// 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());}public Builder() {this(Platform.get());}//对属性的配置......public Retrofit build() {if (baseUrl == null) {throw new IllegalStateException("Base URL required.");}okhttp3.Call.Factory callFactory = this.callFactory;if (callFactory == null) {callFactory = new OkHttpClient();}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> adapterFactories = new ArrayList<>(this.adapterFactories);adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));// Make a defensive copy of the converters.List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,callbackExecutor, validateEagerly);}
}

  对于Retrofit中的属性配置已经在注释中进行说明。在这里在对Platform进行一下介绍。我们可以看出通过Platform.get()来获取到当前运行的平台。下面就进出Platform.get()方法中来查看一下Retrofit到底支持哪些平台。

  private static final Platform PLATFORM = findPlatform();static Platform get() {return PLATFORM;}private static Platform findPlatform() {try {Class.forName("android.os.Build");if (Build.VERSION.SDK_INT != 0) {return new Android();}} catch (ClassNotFoundException ignored) {}try {Class.forName("java.util.Optional");return new Java8();} catch (ClassNotFoundException ignored) {}try {Class.forName("org.robovm.apple.foundation.NSObject");return new IOS();} catch (ClassNotFoundException ignored) {}return new Platform();}

  在Platform的get方法中实际上还是调用了findPlatform方法,通过findPlatform方法我们可以看出Retrofit支持三个平台,它们分别是Android,Java8,IOS。这里的IOS指的是RoboVM。RoboVM它是一种可以在iOS设备上运行Java应用程序的技术,这种技术主要还是用于在游戏开发中。在这里我们只讨论在Android平台中的使用。在获取到当前平台为Android平台之后返回一个Android对象,这个Android类是Platform中的一个静态内部类,并且它继承自Platform。下面就在来看一下这个Android类。

  static class Android extends Platform {@Override public Executor defaultCallbackExecutor() {return new MainThreadExecutor();}@Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {return new ExecutorCallAdapterFactory(callbackExecutor);}static class MainThreadExecutor implements Executor {private final Handler handler = new Handler(Looper.getMainLooper());@Override public void execute(Runnable r) {handler.post(r);}}}

  在这个Android类中重写了父类的defaultCallbackExecutor和defaultCallAdapterFactory方法。
对于defaultCallbackExecutor方法它所返回的一个Executor,从它的实现类MainThreadExecutor可以看出,实际上就是在主线中创建一个Handler对象,然后通过Handler的post方法将请求的结果回调到主线程中运行。而defaultCallAdapterFactory它是默认的Call适配器,它是一个ExecutorCallAdapterFactory对象,对于ExecutorCallAdapterFactory在后面会进行说明。
在这里完成了对于Retrofit对象的创建以后,便通过Retrofit中的create方法创建一个我们请求接口的实现。下面就来看一下这个create方法。

  public <T> T create(final Class<T> service) {Utils.validateServiceInterface(service);if (validateEagerly) {eagerlyValidateMethods(service);}return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },new InvocationHandler() {private final Platform platform = Platform.get();@Override public Object invoke(Object proxy, Method method, Object... args)throws Throwable {// If the method is a method from Object then defer to normal invocation.if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);}if (platform.isDefaultMethod(method)) {return platform.invokeDefaultMethod(method, service, proxy, args);}ServiceMethod serviceMethod = loadServiceMethod(method);OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);return serviceMethod.callAdapter.adapt(okHttpCall);}});}private void eagerlyValidateMethods(Class<?> service) {Platform platform = Platform.get();for (Method method : service.getDeclaredMethods()) {if (!platform.isDefaultMethod(method)) {loadServiceMethod(method);}}}ServiceMethod loadServiceMethod(Method method) {ServiceMethod result;synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {result = new ServiceMethod.Builder(this, method).build();serviceMethodCache.put(method, result);}}return result;}

  在create方法中首先会进行判断所传入进来的Service是否是一个接口,当我们将validateEagerly属性设为true的时候,在我们调用create方法创建一个Service,就直接调用eagerlyValidateMethods方法。而eagerlyValidateMethods的作用是通过反射获取我们创建service接口中所有的接口方法,然后根据接口方法和当前的retrofit对象来获得ServiceMethod并且以接口方法作为Key,ServiceMethod作为值添加到serviceMethodCache缓存中。下次便可以通过接口方法直接获取ServiceMethod。
  现在在往下看我们就明白为什么通过接口来作为一个Http请求,以及为什么调用create方法时需要验证我们的service是否为一个接口。因为在这里是通过Java的动态代理来实现Http请求,并返回一个代理类对象。对于Java的动态代理它是需要委托类与代理类实现同一个接口,在这里也就是我们创建的service请求接口。对于Java的动态代理可参考Java设计模式之代理模式这篇文章。当我们通过代理类(也就是我们调用create方法后返回的service)调用我们所创建的接口方法时。InvocationHandler中的invoke方法将会被调用。在invoke方法中由于method.getDeclaringClass()获取到的是一个接口,并不是Object类,所以第一个条件不成立。而在Android平台下platform.isDefaultMethod(method)返回的为false,所以这个条件也不成立。之后通过loadServiceMethod方法来获取ServiceMethod。最后调用ServiceMethod中callAdapter的adapt方法。而ServiceMethod中的callAdapter属性是通过ServiceMethod中createCallAdapter方法所创建。在createCallAdapter中实际上是调用了Retrofit中的callAdapter方法来对ServiceMethod中的callAdapter进行初始化。下面再看一下Retrofit中的callAdapter方法。

  public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {return nextCallAdapter(null, returnType, annotations);}public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,Annotation[] annotations) {checkNotNull(returnType, "returnType == null");checkNotNull(annotations, "annotations == null");int start = adapterFactories.indexOf(skipPast) + 1;for (int i = start, count = adapterFactories.size(); i < count; i++) {CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);if (adapter != null) {return adapter;}}......}

  在这段代码中,通过遍历adapterFactories并根据我们的接口方法所中返回值类型来获取响应的适配器callAdapter。例如上面的例子中返回值是一个Call对象,将会采用默认的适配器,如果我们返回的是RxJava中Observable对象,如果我们添加了RxJavaCallAdapterFactory,那么返回的就是RxJavaCallAdapter。如果没有添加那么此处的adapter为null,便会抛出异常。在正是通过这种适配器模式完成了对RxJava的完美结合。下面就以Retrofit默认的callAdapter为例来看一下是如何调用OKHttp来完成对网络的请求的。对于此时的callAdapter就是通过platform.defaultCallAdapterFactory(callbackExecutor)所创建的适配器。从刚才观察Platform内部类Android中可以看出它返回的是一个ExecutorCallAdapterFactory对象。下面就来看一下这个ExecutorCallAdapterFactory类。

final class ExecutorCallAdapterFactory extends CallAdapter.Factory {final Executor callbackExecutor;ExecutorCallAdapterFactory(Executor callbackExecutor) {this.callbackExecutor = callbackExecutor;}@Overridepublic CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {if (getRawType(returnType) != Call.class) {return null;}final Type responseType = Utils.getCallResponseType(returnType);return new CallAdapter<Call<?>>() {@Override public Type responseType() {return responseType;}@Override public <R> Call<R> adapt(Call<R> call) {return new ExecutorCallbackCall<>(callbackExecutor, call);}};}static final class ExecutorCallbackCall<T> implements Call<T> {final Executor callbackExecutor;final Call<T> delegate;ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {this.callbackExecutor = callbackExecutor;this.delegate = delegate;}@Override public void enqueue(final Callback<T> callback) {if (callback == null) throw new NullPointerException("callback == null");delegate.enqueue(new Callback<T>() {@Override public void onResponse(final Call<T> call, final Response<T> response) {callbackExecutor.execute(new Runnable() {@Override public void run() {if (delegate.isCanceled()) {// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.callback.onFailure(call, new IOException("Canceled"));} else {callback.onResponse(call, response);}}});}@Override public void onFailure(final Call<T> call, final Throwable t) {callbackExecutor.execute(new Runnable() {@Override public void run() {callback.onFailure(call, t);}});}});}......
}

  从ExecutorCallAdapterFactory类中可看出通过get方法返回一个CallAdapter对象,从对CallAdapter的实现中可以看出在CallAdapter中的adapt方法返回的是ExecutorCallbackCall对象。它实现了Retrofit中的Call接口。到这里我们也就明白了,当通过代理调用我们创建的接口方法中所返回的Call对象就是这个ExecutorCallbackCall。当我们通过call.enqueue来完成网络请求操作实际上就是调用ExecutorCallbackCall中的enqueue方法。在ExecutorCallbackCall中enqueue又将网络请求委托给OkHttpCall去执行。而这个OkHttpCall正是我们在Retrofit的create方法中所创建的OkHttpCall。由于OKHttp的CallBack接口中的onResponse和onFailure是在子线程中执行的,所以在这时候又通过callbackExecutor将CallBack的onResponse和onFailure切换到主线程中执行。

总结

  在这里对于Retrofit2的源码分析就结束了,在这里我们只分析了通过GET方式进行异步请求这一种情况,对于同步,以及POST请求等原理类似,在这就不在进行详细说明。在Retrofit2中我们可以发现它的代码虽然不多,但是却大量用到了Java中的设计模式。很是值得我们学习。

Retrofit2源码解读相关推荐

  1. Bert系列(二)——源码解读之模型主体

    本篇文章主要是解读模型主体代码modeling.py.在阅读这篇文章之前希望读者们对bert的相关理论有一定的了解,尤其是transformer的结构原理,网上的资料很多,本文内容对原理部分就不做过多 ...

  2. Bert系列(三)——源码解读之Pre-train

    https://www.jianshu.com/p/22e462f01d8c pre-train是迁移学习的基础,虽然Google已经发布了各种预训练好的模型,而且因为资源消耗巨大,自己再预训练也不现 ...

  3. linux下free源码,linux命令free源码解读:Procps free.c

    linux命令free源码解读 linux命令free源码解读:Procps free.c 作者:isayme 发布时间:September 26, 2011 分类:Linux 我们讨论的是linux ...

  4. nodeJS之eventproxy源码解读

    1.源码缩影 !(function (name, definition) { var hasDefine = typeof define === 'function', //检查上下文环境是否为AMD ...

  5. PyTorch 源码解读之即时编译篇

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 作者丨OpenMMLab 来源丨https://zhuanlan.zhihu.com/ ...

  6. Alamofire源码解读系列(九)之响应封装(Response)

    本篇主要带来Alamofire中Response的解读 前言 在每篇文章的前言部分,我都会把我认为的本篇最重要的内容提前讲一下.我更想同大家分享这些顶级框架在设计和编码层次究竟有哪些过人的地方?当然, ...

  7. Feflow 源码解读

    Feflow 源码解读 Feflow(Front-end flow)是腾讯IVWEB团队的前端工程化解决方案,致力于改善多类型项目的开发流程中的规范和非业务相关的问题,可以让开发者将绝大部分精力集中在 ...

  8. spring-session源码解读 sesion

    2019独角兽企业重金招聘Python工程师标准>>> spring-session源码解读 sesion 博客分类: java spring 摘要: session通用策略 Ses ...

  9. 前端日报-20160527-underscore 源码解读

    underscore 源码解读 API文档浏览器 JavaScript 中加号操作符细节 抛弃 jQuery,拥抱原生 JS 从 0 开始学习 GitHub 系列之「加入 GitHub」 js实现克隆 ...

最新文章

  1. 数据读取与数据扩增方法
  2. SqlServer sa 用户登录失败的解决方法
  3. spring data mongo比较两个字段查询
  4. Web前端学习路线分享,初学者不要错过!
  5. 利用火狐浏览器Firebug查看网页相关属性
  6. [LNOI] 相逢是问候 || 扩展欧拉函数+线段树
  7. python输出由1234组成的三位素数_编写程序,输出所有由 1 、 2 、 3 、 4 这 4 个数字组成的素数,并且在每个素数中每个数字只使用依次。_学小易找答案...
  8. 如何使用Java帮助文档
  9. 实验五:大数据可视化工具-NodeXL
  10. SQL排序(升序,降序)
  11. 升级Android Q之路遇到的坑-abc_screen_simple
  12. 58同城自动登录。。分享给大家
  13. SE壳C#程序-CrackMe-爆破 By:凉游浅笔深画眉 / Net7Cracker
  14. 使用keras时候,pycharm控制台太多日志,导致最开始的有用信息被覆盖,解决方案
  15. hadoop3.X 和 Hadoop 2.X的web端口号访问
  16. ro服务器物品掉率修改,洪水世界如何调整物品爆率 物品掉率修改方法解析
  17. 序列的算法(一·a)马尔可夫模型
  18. Android6.0 usb默认MTP模式的修改方法
  19. webshell多种方法免杀
  20. Microsonic mic+35/D/TC超声波传感器技术文章

热门文章

  1. 9343拆机 xps13_戴尔(DELL)XPS 13 XPS13D-9343-1808T超极本拆解图评测-ZOL中关村在线
  2. 个人电脑做外网服务器(一)
  3. PHP各大音乐平台API接口下载,PHP各大音乐平台API接口
  4. Android挂断电话以及Java Class Loader
  5. powerdesigner绘制UML模型(包括用例图、类图、时序图、E-R图……)
  6. 《学习之道》第一章开启大门-数学和语言
  7. 共享单车管理系统_共享单车将逐步告别银川!4万辆共享电单车陆续亮相,收费是这样…...
  8. 阜阳一中2021高考成绩查询,安徽阜阳“赫赫有名”的4所高中,成绩说话,2021谁能摘得桂冠?...
  9. DRA成国家标准 音视频步入中国时代
  10. 重庆移动4G资费出炉 600M流量售50元