根据《Retrofit源码解析之请求流程概述》我们知道Retrofit通过OkhttpCall来完成同步或者异步请求,那么OkhttpCall的execute和enqueue方法是什么时候开始执行的呢?这篇博文就来简单的扒一扒其内部原理。

简单以Retrofit同步请求作为说明,简单的例子如下:

interface MyApi {@GetCall<YourBean> getData(params)
}MyApi mApi = retrofit.create(MyApi.class);Call<YourBean> testCall= mApi.getData(params);//*发送网络请求(同步)
Response<YourBean> response = testCall.execute();//获取数据
YourBean bean = response.body();

用法很简单,就是先调用Retrofit的create方法获取一个MyApi对象,然后执行某一个getData方法返回一个Call对象,那么这个Call对象会是OkhttpCall吗?答案是No! 下面就抽丝剥茧来分析下这个Call对象是神马牛鬼蛇神。


先来看看Retrofit的create方法:

public <T> T create(final Class<T> 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, @Nullable Object[] args)throws Throwable {//省略无关代码ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method);OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);return serviceMethod.adapt(okHttpCall);}});}

create方法的入参是一个Interface的Class对象,但是返回的是一个Proxy.newProxyInstance代理对象,因为我们在使用Retrofit的时候只需要定义API接口及其对应的方法,而不需要自己implements API interface.但是接口的方法是用来执行的,你不实现接口怎么能执行呢?代理对象的强大之处就体现出来了:
只需要根据你传入的接口,生成一个代理对象,之后所有的方法调用都会走代理对象invoke方法;且代理对象不需要关心你具体执行方法的名字,一律以method表示. InvocationHandler接口只有一个方法,即invoke()方法!它是对代理对象所有方法的唯一实现。也就是说,无论你调用代理对象上的哪个方法,其实都是在调用InvocationHandler的invoke()方法。我们只需要invoke对Method和args进行处理即可。

可以说Retrofit就是利用代理对象的这一特性,将网络框架的使用简易化:

因为API接口所有方法调用的目的就是把方法的注解信息以及参数信息进行扫描和解析,交给OkhttpCall进行处理执行网络调用

,既然所有的API接口方法调用都是基于同一目的,那么全部就交给代理对象代劳就是了,代理的强大之处被利用的淋漓尽致。

啰嗦了这么多,下面就来看看其invoke方法都做了啥:

 //根据你调用的某个method,调用loadServiceMethod方法。获取对应的ServiceMthod对象ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method);//将参数和Service构成一个OkhttpCall对象              OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);//数据返回
return serviceMethod.adapt(okHttpCall);

步骤很简单明了:
1、根据你调用某个方法method对象从缓存中获取对应的ServiceMethod对象,如果缓存没有则创建一个。
2、将method对应的serviceMethod及其参数args,初始化一个OkHttpCall对象
3、执行serviceMethod的adapt方法,返回一个Call


ServiceMthod的创建

先看看serviceMethod是怎么创建的:

ServiceMethod<?, ?> loadServiceMethod(Method method) {ServiceMethod<?, ?> result = serviceMethodCache.get(method);//缓存中有则使用缓存里面的if (result != null) return result;synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {//没有缓存则创建一个ServiceMethod//this是Retrofitresult = new ServiceMethod.Builder<>(this, method).build();serviceMethodCache.put(method, result);}}return result;}

看看ServieMethod.Builder方法都有啥:

 Builder(Retrofit retrofit, Method method) {this.retrofit = retrofit;this.method = method;//获取Api某个方法的注解信息this.methodAnnotations = method.getAnnotations();//*这个解释有点绕,看https://blog.csdn.net/jiang_bing/article/details/7794365this.parameterTypes = method.getGenericParameterTypes();//获取参数的注解信息this.parameterAnnotationsArray = method.getParameterAnnotations();}

从上面代码可以看出,Builder的构造器的作用就是拿到一个Method对应的信息,比如方法注解信息,参数注解信息等,然后再来看看Buider的build方法:

CallAdapter<T, R> callAdapter;public ServiceMethod build() {//创建一个callAdaptercallAdapter = createCallAdapter();//获取方法的返回类型responseType = callAdapter.responseType();if (responseType == Response.class || responseType == okhttp3.Response.class) {throw methodError("'"+ Utils.getRawType(responseType).getName()+ "' is not a valid response body type. Did you mean ResponseBody?");}//根据returnType创建数据转换器 responseConverter = createResponseConverter();//解析方法注解for (Annotation annotation : methodAnnotations) {parseMethodAnnotation(annotation);}//省略部分注解处理代码:详细解析会另外开启博文说明return new ServiceMethod<>(this);}

CallAdapter方法简单说明
build方法的第一个步骤就是createCallAdapter()创建一个callAdapter,还记得上面代理对象的invoke方法最后一行吗?即return serviceMthod.adapt(OkhttpCall),内部就是调用了callAdapter的adapt方法:

 T adapt(Call<R> call) {return callAdapter.adapt(call);}

所以继续瞅瞅createCallAdapter都干了啥:

 private CallAdapter<T, R> createCallAdapter() {//得到目标方法返回类型对应的Type对象Type returnType = method.getGenericReturnType();//获取方法的注解信息Annotation[] annotations = method.getAnnotations();//根据热天runreturn (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);}}

至于返回哪一个CallAdapter,为了博文的条理性,在这里先直接说结论:
返回是CallAdapte是由从Anrdoid Platfom的ExecutorCallAdapterFactory工厂对象生成的

static class Android extends Platform {@Override public Executor defaultCallbackExecutor() {return new MainThreadExecutor();}@Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {//此处的callbackExecutor就是MainThreadExecutorreturn new ExecutorCallAdapterFactory(callbackExecutor);}static class MainThreadExecutor implements Executor {private final Handler handler = new Handler(Looper.getMainLooper());@Override public void execute(Runnable r) {//使用handle来post一个任务handler.post(r);}}}

ExecutorCallAdapterFactory工厂对象持有一个MainThreadExecutor ,该MainThreadExecutor的作用是将一个任务Runnable都通过handle发送到Android消息队列中去执行

进而看看这个工厂对象返回的CallAdapter是神马:

  @Overridepublic CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {//method方法返回值类型必须是Callif (getRawType(returnType) != Call.class) {return null;}//获取Call<T>的泛型T的类型final Type responseType = Utils.getCallResponseType(returnType);return new CallAdapter<Object, Call<?>>() {@Override public Type responseType() {//Call<JavaBean>那么返回的就是JavaBeanreturn responseType;}//最终invoke会执行的方法//adapt方法参数的call就是OKhttpCall@Override public Call<Object> adapt(Call<Object> call) {//callbackExecutor是MainThreadExecutor return new ExecutorCallbackCall<>(callbackExecutor, call);}};}

上述get方法有几个要注意的地方:
1、Api Service定义的方法的返回值必须是Call《T》类型(当然这是没有结合rx使用的情况)
2、CallAdapter的responseType()方法返回的就是对应的JavaBean的类型,正如注释所说:For example, the response type for {@code Call<Repo>} is {@code Repo}
3、最终CallAdapter的adapt返回的就是ExecutorCallbackCall,而adapt的方法参数就是我们的OKhttpCall(参见文章开头对invoke方法的说明)。也即是说下面的代码中的Call就是ExecutorCallbackCall:

/*实际上返回的就是ExecutorCallbackCall*/
Call<T> call= Api.getData();
Resonse<T> response = call.execute();
T data = response.body();

4、ExecutorCallbackCall持有的callbackExecutor引用指向的对象就是MainThreadExecutor

 final class ExecutorCallbackCall<T> implements Call<T>{//MainThreadExecutor final Executor callbackExecutor;//OKhttpCallfinal Call<T> delegate;ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {this.callbackExecutor = callbackExecutor;this.delegate = delegate;}}

所以文章开头testCall.execute()执行的就是ExecutorCallbackCall的execute方法,而该方法又是代理给OkhttpCall的execute方法:

//同步网络请求public Response<T> execute() throws IOException {//OkhttpCall真正执行的地方return delegate.execute();}

那么在看看其异步网络请求:

 public void enqueue(final Callback<T> callback) {//同样执行的是OKhttpCall的enqueue方法delegate.enqueue(new Callback<T>() {@Override public void onResponse(Call<T> call, final Response<T> response) {callbackExecutor.execute(new Runnable() {@Override public void run() {if (delegate.isCanceled()) {//请求取消callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));} else {//请求成功callback.onResponse(ExecutorCallbackCall.this, response);}}});}//网络请求失败@Override public void onFailure(Call<T> call, final Throwable t) {callbackExecutor.execute(new Runnable() {@Override public void run() {callback.onFailure(ExecutorCallbackCall.this, t);}});}});}

异步请求其实也很简单,其内部就是调用了OkhttpCall的enqueue,只不过在回调结果的时候通过MainThreadExecutor转到了UI线程中去处理了。

到此为止OkttpCall的执行讲解完毕,代码其实很绕,分析其源码的时候各种方法来回跳转,很容易混乱。比如这个OkhttpCall可以直接返回,但是层层代码各种传递才能分析出invoke返回的Call是个什么玩意。为什么本来简单的调用要搞这么复杂呢?这个问题会从代码设计的绝对另开博客详细说明。

总结下来就是:
某ApiService定义的一系烈方法中,你调用了几个个方法,就会对应创建几个ServiceMethod对象,同时也会对应创建几个OkhttpCall来执行。

最后以一个流程图来完成做结:

Retrofit之OkhttpCall执行原理详解相关推荐

  1. MySQL底层执行原理详解

    一.MySQL的内部组件结构 大体来说,MySQL 可以分为 Server 层和存储引擎层两部分. 1.Server层 ​ 主要包括连接器.查询缓存.分析器.优化器.执行器等,涵盖 MySQL 的大多 ...

  2. Mybatis原理——执行原理详解

    总结于B站鲁班大叔视频:https://www.bilibili.com/video/BV1Tp4y1X7FM?p=13&spm_id_from=pageDriver 概述 JDBC的执行流程 ...

  3. SQL底层执行原理详解

    我们平时都是使用sql语句去查询数据,都是很直接的看到结果.那么对于sql底层执行的过程大家有了解吗? 一.MySQL的内部组件结构 大体来说,MySQL 可以分为 Server 层和存储引擎层两部分 ...

  4. Java的加载与执行原理详解 Java程序从编写到最终运行经历了哪些过程

    前言 Java程序从编写到最终运行大概可概括为3个阶段:编写.编译.运行阶段. 一.编写阶段 程序员在硬盘某个位置新建一个xxx.java文件 使用记事本或者其他文本编辑器例如EditPlus打开xx ...

  5. 【异步系列二】Promise原理及执行顺序详解

    前言 Promise 是 javascript 中非常重要的一环,熟悉它是必须的,而且在面试中也常常会问到相关面试题. 在了解 Promise 之前,需要了解什么是异步编程,可以参考我的一篇文章:Ja ...

  6. Oracle SQL语句执行流程与顺序原理详解

    以前读的文章,保存到本地了,忘记来源了,分享一下,本地存着怕丢了 Oracle SQL语句执行流程与顺序原理详解 第一步:客户端把语句发给服务器端执行 当我们在客户端执行SQL语句时,客户端会把这条S ...

  7. jQuery中getJSON跨域原理详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp28 jQuery中getJSON跨域原理详解 前几天我再开发一个叫 河蟹工 ...

  8. 深入剖析Redis系列(三) - Redis集群模式搭建与原理详解

    前言 在 Redis 3.0 之前,使用 哨兵(sentinel)机制来监控各个节点之间的状态.Redis Cluster 是 Redis 的 分布式解决方案,在 3.0 版本正式推出,有效地解决了 ...

  9. SVM-支持向量机原理详解与实践之一

    SVM-支持向量机原理详解与实践 前言 去年由于工作项目的需要实际运用到了SVM和ANN算法,也就是支持向量机和人工神经网络算法,主要是实现项目中的实时采集图片(工业高速摄像头采集)的图像识别的这一部 ...

  10. LVS原理详解及部署之五:LVS+keepalived实现负载均衡高可用

    本文我们主要讲解的是LVS通过keepalived来实现负载均衡和高可用,而不是我们第三篇文章介绍的通过手动的方式来进行配置.通过脚本的方式来显示RS节点的健康检查和LVS的故障切换.此文会通过一个实 ...

最新文章

  1. 青龙面板Nvjdc魔改前段待弹窗---仅支持2.0版本
  2. 2014---努力才是真天赋
  3. vuex的命名空间有哪些_Vuex在vue路由器中访问命名空间模块的getter
  4. Oracle数据备份和恢复
  5. SAP License:O2O模式网站解决方案概述
  6. 二本学医还是学计算机,二本医学院毕业的医学生,最后都去了哪里?看完莫名心酸!...
  7. 一文看懂行业分类--基于wind行业分类标准
  8. NPOI实现Word表格新增一行
  9. 计算机平面设计与工程论文,计算机平面设计毕业论文范文2篇
  10. 无油螺杆鼓风机-市场现状及未来发展趋势
  11. JavaScript 严格模式(strict mode)
  12. 最美人间四月天,我用五个关键词带你回顾
  13. 岩板铺地好吗_岩板铺客厅地面好吗 比800*800的瓷砖更美观又大气?
  14. 焦作机器人编程比赛_2018 焦作icpc现场赛总结
  15. 黑鲨会升级鸿蒙吗,黑鲨4首批用户评价已出炉,不吹不黑,优缺点都很明显!...
  16. Box2D物理引擎入门
  17. 文献记录(part23)--Learn to model blurry motion via directional similarity and filtering
  18. 【NA】矩阵特征值的雅可比方法
  19. firefox os资源
  20. android 动态字幕,Android之如何用TextView实现滚动字幕的效果【跑马灯】

热门文章

  1. php xcache 例子,PHP       xcache
  2. ElementUI:设置table的背景透明、根据表格情况设置背景色、设置文字颜色、文字左右间距、表头、每一行高度
  3. Javascript:利用闭包封装模块
  4. Javascript:学习笔记
  5. 华为算法精英赛(题3:概率计算)
  6. Ubuntu16.04 jsoncpp 的安装
  7. 视觉SLAM十四讲_4-相机模型和非线性优化
  8. 三维重建——相机几何参数标定
  9. 知识蒸馏综述:代码整理
  10. 使用docker私有化部署nuget server-proget