关于Retrofit是啥,这里就不多解释了,还是先来瞅下官网:

而这次主要是了解它的底层动作机制,而在了解底层之前先来回顾一下官网的整体使用步骤:

咱们也以官网的这个例子为例,先从简单的使用开始逐步深入,先新建一个工程:

然后增加retrofit的build引用 ,如下:

然后按官网的步骤,首先创建一个API接口,如下:

咱们以获取用户在github中的仓库为例,定义接口的API方法如下:

然后具体来调用一下,也如官网的描述一样:

然后此时并未发起HTTP请求,需要像okhttp那样调用一下这个方法,分同步和异步,当然这里得用异步喽,如下:

然后增加访问网络的权限:

先来查看一下我的github的仓库:

然后运行一下:

成功了,其实这个接口返回的格式就是json,用浏览器可以访问看下结果:

接下来咱们将结果打印成我们看到的JSON格式一样,而咱们目前成功返回的是一个RsponseBody对像,它是来自okhttp的,如下:

此时就需要注册一个转换器了,这里不细讲怎么用的,直接上结果,重点是通过简单的使用掌握其深层次的本质原理,也就是源码分析,下面来看怎么做这个转换:

那此时怎么写这个转换工厂呢,这时需要再加一个库,也就是gson的支持,关于gson是啥就里就不多说了,直接添加依赖如下:

此时就可以这么用了:

接下来则需要修改API接口了,因为我们不想看到返回的ResonseBody对象,而想看到具体的JSON,从网站上返回的JSON可以看出其实就是一个JSON数组,所以返回的内容应该是一个List,所以修改一下:

然后里面的每个对象则需要我们手动定义出来,先假设这个对像类为Repo,如下:

接下来则定义该类:

然后再定义里面的字段,这里可以通过JSON自动转成Java的字段,可以用JsonFormat工具,如下:

然后将Json数组中的对象内容拷至其中:

接下来咱们来修改一下返回值,如下:

然后运行:

ok,对于retrofit的简单用法就到此结束,重点是接下来分析它的源码:先从使用入口来进行分析的突破口,而使用入口就是它:

能把它分析明白了,那对于retrofit的核心原理也就清楚啦,所以点进去看下它的源码:

那此时就得看调用这个方法的对象是哪个了,如下:

而这个API是咱们定义的接口,也是抽象的。。

那此时就再得往前追溯了,得看它具体的对象:

如果知道了gitHubService的具体对象那么最终我们就可以分析enqueue的具体实现了,所以定位其实现瞅一下:

这个方法是retrofit的核心,其实可以看到有动态代理的东东,所以现在就集中来分析一下该实现:

从字面意思来看是验证服务接口,看下究竟看了啥:

不重要,继续往下读:

这里是一个配置项的检查,表示是否要进行激进化的方法检查,具体就不细看了,不是核心,主要是对我们写的api的方法合法性的检查,如:

如果开启了则会GitHubService一创建就会把所有的验证都做完了,很利于我们的调试,很早就可以发现代码写得不对,但是!!不利于性能,大致知道就行了,继续往下看:

动态代理嘛,难道说retrofit的核心机制就是动态代理?其实确实是它,不过目前还不得而知,关于动态代理是啥这里就不过多解释了,j2se的基础,这里用伪代码来揭露其动态代码的本质,首先看第二个参数:

其实动态代理就是首先生成一个实现了该接口的对象,伪代码表示一下:

然后动态代理不是还有第三个参数InvocationHandler么?如下:

其实它就会传到动态生成的代理对象里面,然后在每个具体方法实现中则会用到它来生成,伪代码如下:

如果说我们在API接口中定义了多个方法,则在这个动态生成的对象中的实现也都是用invocationHandler来实现的,这就是动态代理的本质。

那接下来就把精力花在这个invoke方法的具体实现上了,只要分析清楚了它,那么就知道为啥我们仅仅声明一个API接口retrofit就可以实现一个网络请求了,所以,研究一下invoke方法的具体实现:

而如果调用的是接口中的默认实现方法【这是Java8才有的】,直接也不做其它任何处理了,对于使用retrofit而言不可能有这种默认方法,所以可以略过这个判断细节,继续往下探究:

好晕呀,这三行中涉及到完全陌生的ServiceMethod、OkHttpCall,完全不明白,这里就涉及到一个读源码的小技巧了,对于都看不懂的情况下,先对涉及到的类都大至认识一下既可,不用深究,所以咱们一个个先来大致瞅一下:

啥意思?首先得理解一下什么是adapter,这个在我们listview的开发中必用的概念,还是先看一下它词的本义:

也就是做转接用的,也就是可以猜测ServiceMethod的作用是:

然后此类的代码量太大,也没法继续往下看了,还是返回到主调代码处继续了解其它的东东,继续看下它:

然后咱们来看一下ServiceMethod是如何生成的,通过生成细节看是否能进对ServiceMethod有一个进一步的了解,如下:

然后再看一下build()方法的细节:

然后再通过构造来实例化:

很经典的Builder模式,不过整个构建对象的细节完全看不懂,先暂且放着,等回过头按需再来查看,先来说一下Builder模式,人人皆知,这里简单说一下它的好处,通常我们用Builder模式通常会这样写:

那它有啥好处呢?对于Person中有字段是有初始化成本的,什么意思?比如我们用正常的方式来初始化会这样写:

首先就在内存中有person对象了,接着再来修改一下性别:

而默认性别是女的,此句执行之后就需要在内存中将女姓给擦掉,然后用这个设置的男性来替代,这是有性能损耗的,接着再来修改年龄:

如果默认年龄是24,那此时内存中又得将24给擦掉然后再画一个31岁的人,再接下来:

默认人是走路的行为,此时又得内存进行擦除改掉用户的行为,所以说这种传统的方式是有性能损耗的,而Builder模式则在构建对象时没有提前生成内存,先生成一个配置清单,最终一起来构建对象,这是它的最大好处之一,另外一个好处就是当参数较多的时候这样写层次也比较清晰,关于builder模式这里简单提一下,还得回到咱们所关心的retrofit实现原理上来:

打开瞅下它是啥?

那不就是说:

所以此时咱们可以看一下enqueue的具体实现:

先跳出这个实现细节,总的来回顾一下:

所以点进入再看最后一行的细节:

没办法,还得硬着头皮点进去瞅下:

那看不懂呀,怎么整,目前我们要了解的这三行代码,前两行大致猜到了一些意思,而最后一行完全不晓得其内部的细节,那接下来就从头来细看一下,看是否通过细看能发现一些线索:

这个之前稍加看过,里在就是维护了一个缓存,不过这里还是要看一下ServiceMethod的创建过程:

其中第一句看到了一个之前的疑问:

其中这上callAdapter是一个接口,所以此时不就解惑了么,所以看一下callAdapter是如何创建的?

跟进去:

再往下跟:

接下来就得看一下这段代码的实现了,先来瞅一下callAdapterFactories对象:

所以看一下它的调用,其实就是在build()方法中,如我们在Activity写的:

所以此时再看一下callAdapterFactories的创建来源:

然后就得看下一句了:

所以。。得看一下"platform.defaultCallAdapterFactory(callbackExecutor)"的细节:

如:

其中我们可以看到其实现中用到了一个“callbackExecutor”,通过它的执行然后再处理的回调:

所以得看一下callBackExecutor是如何传递进来的,此时就又得回调Retrofit.build()方法来了:

然后再进一步跟一下此callbackExecutor的创建细节:

那。。原来我们看到的calladapter的作用是进行线程的转换哦,那我们继续回到ServiceMethod.build()方法分析:

拿我们定义的API接口方法来说就是指的:

接下来往下:

继续往下

这不就是指么:

好,再继续往下:

另外有一个细节需要注意retrofit会对我们写的注解的正确性做验证,会让我们更加规范的使用okhttp,比如multipart需要配合part来使用等,好对于ServiceMethod的build()方法可以发现其实就是对我们定义的API方法进行了解析并存下来,然后再实例化它:

至此,咱们要想分析关键的第一句代码就彻底搞清楚其作用了,回顾一下:

好,接下来再分析核心的第二句代码,比如好理解:

接着再来看第三句代码,其实通过上面的分析也晓得其作用了,挼一下:

然后此时得回顾一下callAdpater是如何创建出来的:

然后此方法的调用是在调用build()时进行的,如下:

所以最终调用adapt()方法的其实是ExecutorCallAdapterFactory里面的了,如下:

也就是最终retrofit动态生成的对像在调用它里面的getRepos()方法返回的是ExecutorCallbackCall对像,如下:

所以接下来我们再来分析最初我们分析不动的方法就顺其自然啦,也就是:

那就是直接调用ExecutorCallbackCall.enqueue()方法,如下:

而代理的call是在我们代理对像方法执行时动态创建的,如下:

所以最终就会转到OkHttpCall.enqueue()方法来,如下:

其中还是利用了ServiceMethod来对之前解析的东东来转换成了okhttp的call,如下:

然后再利用Okhttp的Call进行异步请求,如下:

@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null"); okhttp3.Call call;
Throwable failure; synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true; call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
} if (failure != null) {
callback.onFailure(this, failure);
return;
} if (canceled) {
call.cancel();
} call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
} try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
} @Override public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
} private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}

其中回调是先对OkHttp的Response进行解析,解析成Retrofit的Response:

然后这个parseResponse()方法就可以体现出它与http的关系了,就是用了http的知识来编写的,大致瞅一下:

其中从okhttp的reponse转成retrofit的response最终还用到了converter了,如下:

最后还有一个知识就是retrofit如何集成rxjava,首先得集成一下rxjava,如下:

然后此时需要在增加一个calladapter,如下:

此时我们的API定义返回就不用返回Call对像了,而是可以返回一个Observable,如下:

然后就可以用rxjava的那一套来进行接口请求及返回处理了,Retrofit是可以支持多个Adapter的,瞅一下:

其中我们知道Retrofit默认的Adapter为CallAdapter,是可以将ResponseBody转换成一个Call对象,如下:

其具体实现是:

并达到一个切换线程的作用。

而此时加了一个Rxjava的CallAdapter,如下:

所以我们在api可以为:

到此!!已经完整将Retrofit的整个核心机制分析完了,对于之后在实际工作中用Retrofit也更加踏实了~~说实话还是挺复杂的。

从Retrofit的源码来看 HTTP的更多相关文章

  1. 【转载】retrofit 2 源码解析

    retrofit 官网地址:http://square.github.io/retrofit/ retrofit GitHub地址:https://github.com/square/retrofit ...

  2. 从OkHttp的源码来看 HTTP

    先来了解一下OkHttp的历史,最早是square公司觉得Android给的HttpClient这块的库不太好用,于是乎做了一层包装,再后来他们包装的这个库被Android官方给收回去了,而Andro ...

  3. 从源码来看ReentrantLock和ReentrantReadWriteLock

    上一篇花了点时间将同步器看了一下,心中对锁的概念更加明确了一点,知道我们所使用到的锁是怎么样获取同步状态的,我们也写了一个自定义同步组件Mutex,讲到了它其实就是一个简版的ReentrantLock ...

  4. Java并发——结合CountDownLatch源码、Semaphore源码及ReentrantLock源码来看AQS原理

    前言: 如果说J.U.C包下的核心是什么?那我想答案只有一个就是AQS.那么AQS是什么呢?接下来让我们一起揭开AQS的神秘面纱 AQS是什么? AQS是AbstractQueuedSynchroni ...

  5. spring-cloud-square源码速读(retrofit + okhttp篇)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos spring-cloud-square系列文章 五分钟 ...

  6. Retrofit源码解析(下)

    接着上一章继续分析上一章主要简单说了一下基本使用和注解,这一章,我们主要看源码,废话不多说了,直接上.先上一张图 从网络上拿来的 前面一章说了一下Retrofit的简单使用https://www.cn ...

  7. 源码分析Retrofit请求流程

    Retrofit 是 square 公司的另一款广泛流行的网络请求框架.前面的一篇文章<源码分析OKHttp执行过程>已经对 OkHttp 网络请求框架有一个大概的了解.今天同样地对 Re ...

  8. Retrofit2 源码解析

    原文链接:http://bxbxbai.github.io/2015/12/13/retrofit2-analysis/ 公司里最近做的项目中网络框架用的就是Retrofit,用的多了以后觉得这个框架 ...

  9. co源码解读

    背景: 闲来无事,翻了下co的源码来看,源码短小精悍,算上注释,一共240行左右: 决定写一篇博客来记录下学习的心得. TJ大神的co:https://github.com/tj/co 作用: co通 ...

随机推荐

  1. Arcgis javascript api 动态图层自图层可见性设置

    Arcgis javascript api 动态图层自图层可见性设置 子图层管理 rest服务 sublayers sublayer ArcGISDynamicMapServiceLayer 本文主要 ...

  2. 创建 LVM

    1.将物理磁盘设备条带化为物理卷 # pvcreate /dev/sdb 查看物理卷: # pvs# pvdisplay 2.创建卷组,并添加 PV 到卷组 # vgcreate vg1 /dev/s ...

  3. C++标准模板库STL算法与自适应容器(栈和队列)

    参考<21天学通C++>第23与第24章节,对STL算法与自适应容器进行介绍. 实际上在前面的STL顺序容器.关联容器进行介绍时或多或少引用到了一些STL算法中的模板函数.而自适应容器是在 ...

  4. Redis与memached的区别

    Redis与Memcached的区别 传统MySQL+ Memcached架构遇到的问题 实际MySQL是适合进行海量数据存储的,通过Memcached将热点数据加载到cache,加速访问,很多公司都 ...

  5. JS Maximum call stack size exceeded

    一.问题描述 Maximum call stack size exceeded  翻译为:超过最大调用堆栈大小 二.效果截图 三.问题解决方案 出现该问题,说明程序出现了死循环了.所以要去检查出错的程 ...

  6. springboot集成kaptcha验证码

    在pom.xml引入依赖 <!-- 验证码 --> <!-- https://mvnrepository.com/artifact/com.github.penggle/kaptch ...

  7. Transformer的numpy实现

    下面的代码自下而上的实现Transformer的相关模块功能.这份文档只实现了主要代码.由于时间关系,我无法实现所有函数.对于没有实现的函数,默认用全大写函数名指出,如SOFTMAX 由于时间限制,以 ...

  8. 关于启动kylin报Failed to find metadata store by url: kylin_metadata@hbase的问题解决

    一.出问题的原因 昨天因为项目需要,要将cloudera集群改成高可用,没想到失败了,当时因为冲动手动删了几个hdfs实例的原因,导致退不到原来的状态,折腾了一天,最后终于退回了非HA的集群,但是hd ...

  9. kafka原理分析

    #kafka为什么有高吞吐量 1 由于接收数据时可以设置request.required.acks参数,一般设定为1或者0,即生产者发送消息0代表不关心kafka是否接收成功,也就是关闭ack:1代表 ...

  10. LeetCode 179. 最大数(Largest Number) 21

    179. 最大数 179. Largest Number 题目描述 给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数. 每日一算法2019/5/24Day 21LeetCode179. La ...