站在Stay老司机肩膀上分析Retrofit

几个月前,第一次知道Retrofit是看了Stay的文章:Retrofit分析-漂亮的解耦套路文章看起来高大上的感觉,结果撸了一遍下来,好像很多都是一知半解。虽然老司机开车很稳,但是可能自己内功不足,沿途风景没能好好的欣赏。

现在再看回代码,感觉一下子就明白了。所以自己也打算撸一撸它的流程,自己做一回司机了,新手上路,请坐好扶稳哦(>>>)

首先引用一张Stay画的整体流程图(本来想自己画的,但是看了这张图,感觉自己也画不出更好的了)

Retrofit使用的大量的设计模式,先看看我的代码:

 public static ApiService getDefault() {if (SERVICE == null) {HttpLoggingInterceptor logging = new HttpLoggingInterceptor();logging.setLevel(HttpLoggingInterceptor.Level.BODY);OkHttpClient client = new OkHttpClient.Builder().addInterceptor(logging).build();SERVICE = new Retrofit.Builder().baseUrl(Constants.BASE_URL).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).client(client).build().create(ApiService.class);}return SERVICE;}

很简单,通过Retrofit的Builder创建了一个Service对象,其中配置了一些参数。这里的Retrofit的构造的方式使用了Builder模式。

然后我们点开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);}});}

第一行的 Utils.validateServiceInterface(service) 作用是验证参数service是一个Interface.

结果是返回一个动态代理对象,使用了代理模式。这就意味着你调用service的每一个方法都会被拦截(具体就是你自己声明的API接口)

上面分析的过程图解:

在拦截的时候会进行下列一些处理:

  • 加载API中的方法,解析注解。创建CallAdapter、Converter
  • new一个OkHttpCall,关于Okhttp的所有参数都会在里面完成
  • 用CallApater adapt OkHttpCall。

这里的步骤对应着下面几行代码:

 ServiceMethod serviceMethod = loadServiceMethod(method);OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);return serviceMethod.callAdapter.adapt(okHttpCall);

首先看第一行,点进去loadSeerviceMethod(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;}

这里其实比较简单,就是首先从缓存里面读取ServiceMethod对象,如果没有,就通过Builder模式创建出一个ServiceMethod对象。

接下来我们点进去ServiceMethod的build()方法看看,究竟做了什么:

 public ServiceMethod build() {callAdapter = createCallAdapter();responseType = callAdapter.responseType();...//一系列判断responseConverter = createResponseConverter();for (Annotation annotation : methodAnnotations) {parseMethodAnnotation(annotation);}...//一系列判断return new ServiceMethod<>(this);}

上面的代码我做了简化,其实里面就做了几件事:

首先创建callAdapter,经过我查询源码createdCallAdapter最终返回的就是我们在创建Retrofit对象时addCallAdapterFactory传进去的CallAdapter,接着会创建responseConverter,这个同样是我们创建Retrofit对象时添加的Converter,然后会通过parseMethodAnnotation解析方法上面的注解。

上面分析的过程图解:

接下来解释创建OkhttpCall了,这个没什么好解释的,创建一个对象:

OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

接着就用CallAdapter来适配OkHttpCall了。

return serviceMethod.callAdapter.adapt(okHttpCall);

这里的CallAdapter使用了适配器模式,他会把OkHttpCall返回的结果转换成我们声明的接口的返回值,说到这里我就要解释一下几个CallAdapter,这个CallAdapter实际上是什么东西呢?

上面说过serviceMethod.callAdapter是通过createCallAdapter()得到的,然后最终的值就是我们自己声明的,看回最前面的例子,所用的CallAdapter就是RxJavaCallAdapterFactory.create(),实际上用了Rx的CallAdapter。既然是是策略模式,当然有几种策略可供选择啦。

CallAdapter在Retrofit中提供了四种,分别为:RxJavaCallAdapterFactory、Java8CallAdapterFactory、GuavaCallAdapterFactory、AndroidCallAdapterFactory。

前面的例子使用的是RxJavaCallAdapterFactory。所以我们打开RxJavaCallAdapterFactory的源码看看:

private CallAdapter<Observable<?>> getCallAdapter(Type returnType, Scheduler scheduler) {Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);Class<?> rawObservableType = getRawType(observableType);if (rawObservableType == Response.class) {if (!(observableType instanceof ParameterizedType)) {throw new IllegalStateException("Response must be parameterized"+ " as Response<Foo> or Response<? extends Foo>");}Type responseType = getParameterUpperBound(0, (ParameterizedType) observableType);return new ResponseCallAdapter(responseType, scheduler);}if (rawObservableType == Result.class) {if (!(observableType instanceof ParameterizedType)) {throw new IllegalStateException("Result must be parameterized"+ " as Result<Foo> or Result<? extends Foo>");}Type responseType = getParameterUpperBound(0, (ParameterizedType) observableType);return new ResultCallAdapter(responseType, scheduler);}return new SimpleCallAdapter(observableType, scheduler);}

从上面代码看来,实际上会根据Method不同的返回值类型来返回不同的CallAdapter,比如返回值是Response的话就会返回ResponseCallAdapter,返回值是Result就会返回ResultCallAdapter,返回值是普通Object的话就回返回SimpleCallAdapter。

这三个CallAdapter最后都会调用adapt方法,adapt方法的作用就是把执行请求并且把结果发射出去:

//adapt方法@Override public <R> Observable<Response<R>> adapt(Call<R> call) {Observable<Response<R>> observable = Observable.create(new CallOnSubscribe<>(call));if (scheduler != null) {return observable.subscribeOn(scheduler);}return observable;}//CallOnSubscribe -> RequestArbiter@Override public void request(long n) {if (n < 0) throw new IllegalArgumentException("n < 0: " + n);if (n == 0) return; // Nothing to do when requesting 0.if (!compareAndSet(false, true)) return; // Request was already triggered.try {Response<T> response = call.execute();if (!subscriber.isUnsubscribed()) {subscriber.onNext(response);}} catch (Throwable t) {Exceptions.throwIfFatal(t);if (!subscriber.isUnsubscribed()) {subscriber.onError(t);}return;}if (!subscriber.isUnsubscribed()) {subscriber.onCompleted();}}

最终会调用了call.execute();这个call其实就是我们上面在动态代理new出来的OkHttpCall,然后OkHttpCall的execute()方法:

@Override public Response<T> execute() throws IOException {okhttp3.Call call;synchronized (this) {if (executed) throw new IllegalStateException("Already executed.");executed = true;if (creationFailure != null) {if (creationFailure instanceof IOException) {throw (IOException) creationFailure;} else {throw (RuntimeException) creationFailure;}}call = rawCall;if (call == null) {try {call = rawCall = createRawCall();} catch (IOException | RuntimeException e) {creationFailure = e;throw e;}}}if (canceled) {call.cancel();}return parseResponse(call.execute());}

最后调用了parseResponse()方法,最终执行:

T body = serviceMethod.toResponse(catchingBody);//ServiceMethod:
T toResponse(ResponseBody body) throws IOException {return responseConverter.convert(body);}

其实就是调用我们的GsonConverter来convert把数据转换成对象而已。

好了这样的一个过程就分析完毕了。

上面分析的过程图解:

最后我们分析一下Retrofit的build方法:

  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);}}

可以看到callFactroy为null的情况下会创建一个OkHttpClient(),所以没有指定client的情况下会使用okhttpclient请求网络。如果我们没有指定CallAdapter的话,也会默认生成一个

总结

上面的总结可能有些地方混乱,因为我是结合自己的代码例子来分析整个过程的,而且对于上图右边的其他一些GuavaCallAdapterFactory、Java8…等等都没有分析,不过大体上的套路就是上面这样分析,我因为是结合例子分析,使用的是RxJava,自然就只分析到RxJavaCallAdapterFactory了,整体的流程大概就拎出来了。

分析完了,还要思考一下作者为什么要做,说解耦大家都知道! 可能还是内功不够,所以思考不出什么来呀,暂时不想它了吧。嗯,就这样了。

年轻的司机开车真是每一次都这么激动。

最后我在分析代码的时候,发现Stay的图有一点点画的不太对,我在下图把图片标注出来:

站在Stay老司机肩膀上分析Retrofit相关推荐

  1. 60个Chrome神器插件大收集:助你快速成为B站老司机,一键分析网站技术栈

    乾明 编辑整理  量子位 报道 | 公众号 QbitAI 让WebP图片下载为PNG格式,从Github批量下载表情包,一键滚动截屏整个网页,助你快速成为B站老司机,一键分析网站技术栈,翻录网页视频神 ...

  2. 媒体报道丨激光雷达老司机踏上创业路,饮冰科技如何杀出重围?

    文 | 于寒 来自投资家网(touzijias)的报道 刚刚过去的人工智能(AI)元年,涌现了很多AI明星公司,尤其是在自动驾驶领域,忽如一夜春风来,千树万树梨花开. 除了BAT等多个互联网巨头之外, ...

  3. 老司机教你分析日志:分析用户的地理位置信息

    摘要: 地理位置的需求 通常我们分析用户的需求,了解到用户当前位置在哪里非常重要,例如,可以根据用户的地理位置,针对性的推广本地广告. 通常,我们可以在客户端获取定位权限来获取GPS信息.但是如果用户 ...

  4. 如何站在双11的肩膀上 详解阿里云企业级互联网架构

    摘要:2015天猫双11全球狂欢节以912.17亿元的答卷完美收官.双11当天14万笔/秒的交易创建峰值和8.6万笔/秒的支付峰值双双刷新了世界纪录,相比首届双11,订单创建峰值增长了350倍,支付峰 ...

  5. 站在一哥们巨人肩膀上,开发完善了闲鱼上新监测软件

  6. EOS原力孤矢:站在EOS肩膀上,看齐BTC

    FBEC2018 由深圳市互联网文化市场协会指导,游戏陀螺.陀螺财经联合主办的2018未来商业生态链接大会暨第三届金陀螺奖颁奖盛典(简称"FBEC2018")将于2018年12月在 ...

  7. 博客园设置好看的主题!站在巨人的肩膀上眺望远方!!

    [前言] 昨天(2020.12.6)学习期间,突然有篇非常好看的博文映入我的眼帘,说好看,其实是主题好看,当然内容也很优秀.于是乎,我眼馋了,想把自己的博客主题也搞成那个样子,咨询了一下博主大大.他给 ...

  8. 站在巨人的肩膀上-像kubernetes一样用etcd存储自定义对象

    背景 众所周知,kubernetes利用etcd存储API对象,例如Pod.Deployment.StatefulSet等等.笔者认为kubernetes这种API对象的设计方案当前来看非常先进,基于 ...

  9. 除了X站,程序员还喜欢上这些网站...

    点击上方[全栈开发者社区]→右上角[...]→[设为星标⭐] 说到p站,估计不知道的人已经很少了,除了p站还有哪些站?场主盲猜,应该很多人思考过这个问题了  二次元可是有ABCDEFGKNMTP站等诸 ...

  10. 站在BERT肩膀上的NLP新秀们:XLMs、MASS和UNILM

    作者丨高开远 学校丨上海交通大学硕士生 研究方向丨自然语言处理 写在前面 在前一篇站在 BERT 肩膀上的 NLP 新秀们(PART I)[1]介绍了两个都叫 ERNIE 的模型,思路也挺相似的,都是 ...

最新文章

  1. 京瓷2010复印a4内容不全_京瓷2010复印机,纸卡定影的故障
  2. micro/protoc-gen-micro 不见了
  3. Activiti5第十一弹,流程监听器与任务监听器
  4. python-第一个python程序-向世界问好
  5. 活动丨PGConf.Asia大会11月17-20日线上直播!
  6. win7查看tomcat端口_win7
  7. py2neo match
  8. 1020 月饼 (25 分)—PAT (Basic Level) Practice (中文)
  9. 中国老工业基地吉林对外贸易重回历史高水平
  10. Tomcat - SSL操作大全
  11. centos php mysql 配置_CentOS系统中安装配置Apache+PHP+MySQL环境
  12. 疫情之下,你有多久没回过家了?
  13. linux 设置tomcat快捷启动方式
  14. JMeter压力测试和性能优化
  15. 2018年下半年系统集成项目管理工程师真题及答案解析
  16. 微信H5开发-采坑记
  17. 数字图像处理:时域、频域和空间域
  18. vue点击添加一行输入框_vue 点击按钮增加一行的方法
  19. neokylin linux s.iso,NeoKylin-Desktop-Personal-v6.0安装与基本应用实战
  20. Verilog学习之异步复位的串联T触发器设计

热门文章

  1. js逆向教程第二发-猿人学第一题
  2. 零基础学习软件测试必看的python之基础语法
  3. matlab plc控制系统设计,基于MATLAB的PLC温度监控系统设计
  4. 【镜像取证篇】VMware虚拟机配置文件取证
  5. 武汉市查询社保电脑号及公积金账号的方法(湖北省其他市也适用)
  6. 遗传算法java(中国外运杯)
  7. PS 动图修改背景坑
  8. 基于SSM+Mysql在线电影预定下单管理系统
  9. 加载java ie停止工作_IE报错“Internet Explorer 已停止工作”的解决方法
  10. 中继器系列:中继器增删改查