先来了解一下OkHttp的历史,最早是square公司觉得Android给的HttpClient这块的库不太好用,于是乎做了一层包装,再后来他们包装的这个库被Android官方给收回去了,而Android内部的HttpUrlConnection的实现用的其实是OkHttp的代码,而Okhttp是完全重新写的一套HTTP库,包含TCP、TLS的实现,最初是依赖于Google的那套网络框架,而现在完全不依赖了,最终还被Google给收购了,所以,好好了解OkHttp的本质是毋庸置疑的。

先上一下OkHttp的官网瞅下:

看一下它的简单用法:

好,咱们还是以上一节【https://www.cnblogs.com/webor2006/p/10502230.html】获取github的仓库为例,简单用一下OkHttp,如下:

首先增加OkHttp的依赖:

然后来访问这个接口“https://api.github.com/users/webor2006/repos”为例,看下OkHttp是如何来请求的:

运行一下:

其实原因是在build中木有指定Java的编译版本为Java8导致,因为OkHttp库中如今用到了Lambda表达式,所以指定一下:

再次运行:

ok,关于用法就到这,关于OkHttp的具体使用这里就不详细赘述啦,用一用就会了,重点是分析它的原理机制,直接开始看源码了,首先先看下它:

其中Request又是采用Builder模式来构建的,真的是无处不见Builder模式:

点进去看一下源码:

可以发现是由RealCall来返回的,很明显RealCall是Call的具体实现,所以点击RealCall.newRealCall看下它的源码:

其中为啥第二个参数名叫originalRequest,也就是在之后会对这个Request进行转型,所以这是一个最初始的状态;第三个参数涉及到了WebSocket,那啥叫WebSocket呢,百度百科一下:

也就是说它其实是HTTP的一种扩展,正常的HTTP服务器端是不可能主动给客户端消息推送的,而WebSocket一般是炒股交易的软件会用,因为消息会比较实时,通常软件 是不会用到它的,了解一下既可。

接下来再继续:

那这个eventListener有啥用呢?HTTP请求过程中有各种状态,如TCP连接、SSL连接、请求等状态,它主要是用来记录这些状态用的,不重要,貌似这个方法比较简单,接着来就需要分析一下它了:

走进去:

很明显它的实现应该是在RealCall里面如下:

分析核心代码,首先调用eventListener中的一个状态方法:

然后接下来一句就是核心代码了:

这句代码涉及到三个东东:dispatcher()、enqueue()、AsyncCall,所以分别来了解一下:

dispatcher():

那瞅一下Dispatcher类:

而它用到了线程池:

假如想要所有的请求一个个先后执行,那只要将上面的max设置为1既可。

enqueue():

接下来则看一下它的源码:

这里有三个队列定义于Dispatcher类中,如下:

所以说Dispatcher类就是一个任务调度类。

AsyncCall:

enqueue方法参数中还涉及到此类,先来瞅一下:

貌似对于Runnable应该里面会有一个run()方法吧,可AsyncCall中木有看到,如它的结构所示:

那有可能定义在它的父类NamedRunnable中,所以瞅一下:

final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
private volatile AtomicInteger callsPerHost = new AtomicInteger(0); AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}  ... @Override protected void execute() {
boolean signalledCallback = false;
timeout.enter();
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
e = timeoutExit(e);
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}

那这里面应该就是整个请求处理的关键,下面来搞定它:

不过这里暂且不深入分析它,先来总结一下enqueue()的整个流程:它会去调用Dispather.enqueue()方法,而这个方法会生成一个AsyncCall对象,最终会执行AsyncCall.execute()方法,然后最终再真正请求给出结果反馈。

另外对于OkHttp的经典使用除了enqueue()异常请求之外,还有一个execute()方法,它是同步的,也来大致瞅一下它的实现:

也就是enqueue()和execute()就是是否是同步的区别而已。

好,接下来则需要研究两个东东了:

一是OkHttpClient在实用角度(理解OkHttp跟Http的关系)需要理解一下它的配置项;

二是上面提到的拦截链。

好,先来瞅一下OkHttpClient这个类:

这个就是客户端告诉OkHttp支持HTTP的版本,就像浏览器能支持HTTP1.0、HTTP1.2,这个属性就是干这个用的。

所以这个是告诉OkHttp是否支持HTTP,还是支持HTTPS,而HTTPS要怎么支持,也就是对于HTTP的底层支持OkHttp全部都实现了,这就可以看出跟Retrofit的一个区别了,好,继续!

关于这俩在之后再详说,跟拦截器链有关,往下继续:

这个还记得么,回顾一下:

也就是它是用来创建eventListener的东东,往下:

关于它的使用场景在之前HTTPS中已经学习过了,这里简单点击瞅一下:

它是JDK中的接口,其中OkHttp的实现如下:

就不细看了,继续往下看:

对于有自签名的需求用它是非常合适的,这里来简单了解一下它的用法,文档上有描述,如下:

咱们来贴过来试一下:

其中增加了一个验证的选项,这样就不会使用本地根证书进行验证了,就会使用咱们自己的验证方式了,如下:

此时肯定会验证失败,运行如下:

03-15 23:01:48.172 24248-24267/com.okhttpstudy.study E/cexo: Failed!!!Certificate pinning failure!
Peer certificate chain:
sha256/uL6J3XldY7njtfGsRP7HZgYsrNPrDZXpG7kT7wfg1m0=: CN=www.baidu.cn,OU=service operation department,O=BeiJing Baidu Netcom Science Technology Co.\, Ltd,L=Beijing,C=CN
sha256/5kJvNEMw0KjrCAu7eXY5HZdvyCS13BbA0VJG1RSP91w=: CN=DigiCert SHA2 Secure Server CA,O=DigiCert Inc,C=US
sha256/r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=: CN=DigiCert Global Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US
Pinned certificates for baidu.com:
sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

其会在错误中会将此网站的所有签名信息都会列出来,此时要想自签名让其通过验证,则将错误提示的三个sha值添加到okhttpclient上,如下:

这个当有自签名的需求时还是比较好用滴。再继续:

那具体是怎么用它的呢?

简单瞅下它:

至此整个OkHttpClient的字段都过了一遍,很明显通过了解这些字段的含义能知道OkHttp底层做了好多事,清楚的能看到到处有http的身影。

接下来则需要研究Okhttp最核心的拦截器链啦,如下:

点进去瞅瞅它的实现:

其中首先是准备各种interceptor,如下:

接着将其生成一个InterceptorChain对像,如下:

用图来表达一下拦截器链:

最后执行拦截器链的proceed(),此时其实是这么个过程:

为啥要一个链呢?这里可以举一个汉堡相关的例子用来理解:有人打电话进来订了汉堡的餐,然后店老板将汉堡做好了并且给负责分发的员工,接着送餐员到店取了汉堡骑着电动送到订餐人的家里,然后送餐员将汉堡给了订餐的人,与此同时订餐人将钱给了送餐员, 送餐员又拿着钱回到了店里,并从中拿了1块钱的提成,然后将剩下的钱全部交给了负责分发的店员,然后店员又将钱交给了店长,这就是一个链,店长是链的起点【做汉堡并分配给店员】和终点【从店员中拿到钱】。当一个事件比较复杂的时候采用链的方式会职责清晰,每一个链则就是拦截器。

其中关于proceed()这里还是以上面举的汉堡的店长为例进一步阐述一下:其实对于店长的proceed()过程就是“做汉堡并分发给店员--->等待收钱---->收钱之后装进自己的裤腰袋”,而结合图的第一个节点表示店长的链,其过程其实是这样:

此时店长proceed()了,则会将链接给下一个接点,也就是分发的店员,如下:

接着经过漫长的等待,最终会从店员中收到钱,此时节点就会回到了如下位置:

用更加形象一点的图来表示整个proceed()的过程,其实有点像递归:

从中可以看到链上的每一个结点都执行proceed()之后则就行成了一个一去一回的完整链的过程。

大概理解拦截链之后,接下来则细看一下具体的各个链,对于OkHttp的链,有两个可以自动配置的,如下:

这个先放一下,先来看一下其它具体的拦截器

RetryAndFollowUpInterceptor:

第一个是它:

重试及重定向拦截器,点进去,对于每一个拦截器主要是看它的intercept()方法,所以:

其里面做的事情基本上如我们上面理论所说会有如下三件事件【OkHttp所有链的工作方式都遵循此原则,所以理解它非常之重要!】:

1、最初的准备(准备汉堡并交给店员)。

其中瞅一下StreamAllocation:

基本上这个拦截器前置工作比较简单,重点是后置工作。

2、等待结果(等待收钱)。

接着执行proceed():

此时就会等待它之后的所有节点都处理完之后,此proceed()才会返回结果。

3、处理结果(将钱装自自己口袋)。

也就是proceed()之后的则是处理结果,具体来瞅下此拦截器的后置处理:

所以瞅一下recover方法:

如果符合重试条件,那么直接再次循环:

其它的重试处理也类似:

最终:

BridgeInterceptor:

点进来瞅下,能很清楚的看到http的影子,如下:

好,还是按之前的三步骤来分析:

1、最初的准备(准备汉堡并交给店员)。

基本全是加头信息:

也就是OkHttp自身就已经支持gzip压缩解压缩,也就是开发者完全透明,具体解的地方在:

这个是需要开发者自己定义的,因为CookieJar默认是空实现的,如下:

其大致用法可以这样写:

2、等待结果(等待收钱)。

当然就是执行proceed()方法喽:

3、处理结果(将钱装自自己口袋)。

基本上做解工作,在proceed()之后:

CacheInterceptor:

比较简单,就直接过了。

ConnectInterceptor【真正跟http或https进行交互了】:

点进去瞅下细节,主要是看流程,因为这个流程能够让我们清楚的发现跟TCP和SSL的建立过程:

好,目前TCP的连接已经看到了,那SSL的连接呢?得回到上一层,这:

所以此拦截器的作用就是用来建立TCP+SSL连接用的,而它木有后置工作:

CallServerInterceptor:

因为它是最后一个拦截器了,所以木有proceed()了,做完事情直接返回就ok了,它都是做的实质工作,下面也来分析其关键代码:

我们看http1的就成:

也就是通过TCP来往服务器建立通信了,接着来处理response并返回:

最后就只剩自定义的拦截器木有看了,这个比较简单,首先可以配置一个自己的拦截器,如下:

对于我在公司的项目通常会用这个拦截器来打印一下日志,或者传一些公共头信息之类的,总之比较好用。

那对于这两个自定义的拦截器,有啥区别呢?

所以如果想直接http数据时则需要用networkInterceptors,一般用不到它,主要是用上面的自定义拦截器。

到此,整个完整的链都分析完了,可以看到OkHttp是整个接管了整个Http的工作,从TCP连接的建立,TLS连接的建立,HTTP报文相关的处理,而Retrofit是利用Okhttp来实现的,只是API做了很多的容错。

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

  1. 从Retrofit的源码来看 HTTP

    关于Retrofit是啥,这里就不多解释了,还是先来瞅下官网: 而这次主要是了解它的底层动作机制,而在了解底层之前先来回顾一下官网的整体使用步骤: 咱们也以官网的这个例子为例,先从简单的使用开始逐步深 ...

  2. okhttp框架源码分析从同步&异步请求使用开始

    对于okhttp在如今项目中的普及程度已经不言而喻啦,基本上如今网络请求都会基于它去进行封装,而非前几年用Android的网络框架HttpURLConnection和Apache HttpClient ...

  3. 从源码来看ReentrantLock和ReentrantReadWriteLock

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

  4. Android八门神器(一): OkHttp框架源码解析

    HTTP是我们交换数据和媒体流的现代应用网络,有效利用HTTP可以使我们节省带宽和更快地加载数据,Square公司开源的OkHttp网络请求是有效率的HTTP客户端.之前的知识面仅限于框架API的调用 ...

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

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

  6. 从设计模式角度看OkHttp源码

    前言 说到源码,很多朋友都觉得复杂,难理解. 但是,如果是一个结构清晰且完全解耦的优质源码库呢? OkHttp就是这样一个存在,对于这个原生网络框架,想必大家也看过很多很多相关的源码解析了. 它的源码 ...

  7. Android:手把手带你深入剖析 Retrofit 2.0 源码

    前言 在Andrroid开发中,网络请求十分常用 而在Android网络请求库中,Retrofit是当下最热的一个网络请求库 今天,我将手把手带你深入剖析Retrofit v2.0的源码,希望你们会喜 ...

  8. android开源新闻小程序、3D翻转公告效果、小说检索、Kotlin开发TODO清单等源码

    Android精选源码 开源新闻小程序源码分享 android动态壁纸.锁屏动画.来电秀等源码 android笔记App效果源码 Android实现3D版翻页公告效果 android小说搜索阅读源码 ...

  9. co源码解读

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

随机推荐

  1. cordon、drain、delete node区别

    cordon.drain.delete node区别 主要目的:导致node处于不可调度状态,新创建的pod容器不会调度在node上. cordon将node置为SchedulingDisabled不 ...

  2. _string 灵活查询

    $process = (int)$_POST['process']; switch ($process) { case 0: // 全部 $where['_string'] = ' (`sale_wa ...

  3. ubuntu 安装 iperf

    iperf的github https://github.com/esnet/iperf/releases 解压 sudo tar -zvxf iperf-3.6.tar.gz -C /usr/loca ...

  4. Practice

    一.简介 Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中.其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的 ...

  5. triples I(按位或运算及3的特性)(2019牛客暑期多校训练营(第四场)D)

    示例1: 输入: 2 3 7 输出: 1 32 3 6 说明:3=3, (3|6)=7 题意:输出尽可能少的数字,他们的按位或结果为输入的数字a. 题解:(表示看不懂题解,看山东大佬的代码看懂的)首先 ...

  6. linux 列出本文件下的目录

    `ls /etc` 和$(ls /etc) 是两种获取命令执行结果的方式. for file in List 的语法里面的List 就是使用使用ls /etc 的命令执行结果作为List(https: ...

  7. 【Qt】Qt5.12编译MySQl5.7驱动(亲自测试成功)

    目录 00. 目录 01. 安装Qt5.12 02. 打开MySQL源码项目 03. 编译MySQL驱动代码 04. 修改mysql.pro文件 05. 编译之后得到对应的库 06. 拷贝动态库到指定 ...

  8. mdk编译时的内存分析

    内存四区(代码区,全局区,栈区,堆区) Code:即代码域,它指的是编译器生成的机器指令,这些内容被存储到ROM区. RO-data:Read Only data,即只读数据域,它指程序中用到的只读数 ...

  9. ASP.net Web API综合示例

    目录 概述 功能介绍 程序结构 服务器端介绍 客户端介绍 “契约” Web API设计规则 并行写入冲突与时间戳 身份验证详解 Web API验证规则 客户端MVVM简介 Web.Config 本DE ...

  10. poj 3252 数位dp

    题意:一个二进制的数,如果0的个数大于1的个数,那么我们称这个数为Round Numbers,求给定区间(十进制表示)中Round Numbers的个数 题解:数位dp,不过这里枚举的时候lead标记 ...