前面介绍了网络的基础知识,这篇主要从OKHttp源码角度来分析Http。OKHttp是一个优秀的网络请求框架,有以下特点:
- 支持HTTP2/SPDY
- Socket自动选择最好路线,并支持自动重连
- 拥有自动维护的Socket连接池,减少握手次数
- 拥有队列线程池,轻松写并发
- 拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩)
- 实现基于Headers的缓存策略
基本使用
同步请求
同步的Get请求
![76c86eb8a7b4f4ae2395c65a16326c57.png](https://i-blog.csdnimg.cn/blog_migrate/eec66c6d1bb691a977f4e970f671f508.jpeg)
异步请求
异步的Get请求
![32985450c0105b768bc2487b4844c271.png](https://i-blog.csdnimg.cn/blog_migrate/ac99bb2189dc149155bed3347e4449e6.jpeg)
源码分析
我们从OKHttp的初始化开始分析。
OkHttpClient
新建一个OkHttpClient对象
OkHttpClient client = new OkHttpClient();
构造函数声明:
![e4379adaaf818b41830e70654f8b87c1.png](https://i-blog.csdnimg.cn/blog_migrate/c81e167b1f3f304dce76a578caae404d.jpeg)
Builder模式构造:
![f6a6dd3155f4315ce03c5d7b8fd6e30a.png](https://i-blog.csdnimg.cn/blog_migrate/949e18ba41bb055daffb1d162f0f6fb5.jpeg)
声明了很多属性,具体含义,等后面用到在具体介绍。
请求流程
请求流程可分为同步和异步,大体的请求流程如下图所示:
![5ae480b5e9ee89c7c4cbf1646c1c595b.png](https://i-blog.csdnimg.cn/blog_migrate/7a7135fcd857cce8a2986e689a89df93.jpeg)
OKHttp流程
同步请求流程
client.newCall(request).execute();
newCall返回的是RealCall,上面代码实际上执行的是RealCall的execute方法。
![decd3699de4191cebaa55cc0a9c1e9ec.png](https://i-blog.csdnimg.cn/blog_migrate/e03aa66e7d0e7a2156be654f8e8ffd49.jpeg)
- executed判断Call对象是否已经执行,每个Call对象只能执行一次
- client.dispatcher()返回Dispatcher对象,任务核心调度类,是OKHttp中最重要类之一, executed方法把该线程添加到同步线程队列synchronized void executed(RealCall call) { runningSyncCalls.add(call); }
- getResponseWithInterceptorChain()获取HTTP请求结果,并会进行一系列拦截操作
- client.dispatcher().finished(this)执行完毕操作
![729722c40cc435ac2b7c27f5540595b9.png](https://i-blog.csdnimg.cn/blog_migrate/e59ac4ce3019c10390e0308e91ed06ce.jpeg)
执行完毕后,会把线程从同步线程队列中移除:
![0d3d7f8e5592689c3b998f30e3937976.png](https://i-blog.csdnimg.cn/blog_migrate/ad34ba92415381709ecad3ad989ade64.jpeg)
异步请求流程
RealCall的enqueue方法:
![b72844d4b66c0ea2445e66c159ae15d7.png](https://i-blog.csdnimg.cn/blog_migrate/1f2dbf96d1ee7fc9d3cbc5b2fd5b8e55.jpeg)
- executed含义和同步请求一样,表示请求只能执行一次
- client.dispatcher().enqueue(new AsyncCall(responseCallback));,会生成一个AsyncCall对象,并把它加入到readyAsyncCalls线程队列中,等待执行
AsyncCall是RealCall的内部类,并且是NamedRunnable线程类,具体执行方法:
![82595b3f89298631a4bbe9ee6c3730be.png](https://i-blog.csdnimg.cn/blog_migrate/c760c7494ede45aa85456f0180234d9e.jpeg)
- getResponseWithInterceptorChain()获取HTTP请求结果,并会进行一系列拦截操作
- client.dispatcher().finished(this);这个方法很重要,和同步方法中调用类似,但是异步的流程则完全不同
finish方法:
![f0b366ba2fd71ec06f18a54cb85dacea.png](https://i-blog.csdnimg.cn/blog_migrate/a227ddedad34217a5bad1754f74468a6.jpeg)
![545203a0f6044e4b7ec681df607844a9.png](https://i-blog.csdnimg.cn/blog_migrate/8f620a0015e61ea4248ff53220bddac5.jpeg)
异步流程中,promoteAndExecute方法:
![d95a4c54ae576e2c34feda453805a3eb.png](https://i-blog.csdnimg.cn/blog_migrate/94d5e84db901eac82556430f4c219582.jpeg)
会遍历异步等待线程队列,并对正在执行的异步线程队列进行最大请求size,以及每个host最大请求size进行检查。把异步等待线程放到正在执行线程队列中,并在等待线程队列中删除该线程,这样就把等待线程变成正在执行线程。
Dispatcher
任务调度核心类,这个类,其实在同步和异步请求流程中已经介绍过,其最重要功能是负责请求的分发。Dispatcher在OKHttpClient的Builder中被初始化:
![14873b241030fe52b104beb35b10ef90.png](https://i-blog.csdnimg.cn/blog_migrate/66b8a7d041576d481b1c9ac71ac9fd8c.jpeg)
- maxRequests:最大请求并发请求数64
- maxRequestsPerHost:每个主机的最大请求数5
- executorService:线程池
- readyAsyncCalls:异步等待线程队列
- runningAsyncCalls:正在运行的异步线程队列
- runningSyncCalls:正在运行的同步线程队列
线程池executorService的声明:
![f57c8b9f497beb1e44c088006c8fdb2e.png](https://i-blog.csdnimg.cn/blog_migrate/c9bc11e162c8d6b2689b314bf48d27dd.jpeg)
- 核心线程数为0,表示线程在空闲时不会被保留,等待一段时间后停止
- 最大线程数Integer.MAX_VALUE,基本上就是可以创建线程无上限
- keepAliveTime为60s,表示如果线程空闲时,最多只能存活60s
综合上诉,在OKHttp中,设置了不设上限的线程,不保留最小线程,线程空闲时,最大存活时间为60s,保证I/O任务中高阻塞低占用的过程,不会长时间卡在阻塞上。并通过maxRequests和maxRequestsPerHost来控制并发最大请求数。
拦截器
在同步和异步请求中,具体的执行过程中都会调用到getResponseWithInterceptorChain方法,该方法添加了一系列的拦截器,它在OKHttp整理流程中处于非常重要的地位,
![2c329a4c3d1638a4ca08929d95ef7c9f.png](https://i-blog.csdnimg.cn/blog_migrate/5735d52563157deee1835054cd8f575f.jpeg)
流程
方法实现:
![16dba29982db94c110d600f0028a7734.png](https://i-blog.csdnimg.cn/blog_migrate/47a0a1bf87dacc5d67922fc264048aa6.jpeg)
默认添加的拦截器:
- RetryAndFollowUpInterceptor:负责失败重试以及重定向
- BridgeInterceptor:负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应
- CacheInterceptor:负责读取缓存直接返回、更新缓存
- ConnectInterceptor:负责和服务器建立连接
- CallServerInterceptor:负责向服务器发送请求数据、从服务器读取响应数据
这是典型的责任链模式,通过Interceptor,把Request转换为Response,每个Interceptor都有各自的责任和逻辑。
![cd69a7aae0a1fdf95c8b9343c3e1319d.png](https://i-blog.csdnimg.cn/blog_migrate/75fe552f77b317a44f7e5aa6c3a02408.jpeg)
开发者可以自己定义Interceptor,在最开始或者发送请求前,对Request和Response进行处理。
HTTP实现
OKHttp中实现HTTP主要是在ConnectInterceptor和CallServerInterceptor。ConnectInterceptor建立服务器之间的连接,CallServerInterceptor发送请求和读取响应。OKHttp请求一个URL的流程:
根据请求的URL,createAddress方法会创建一个Address,用于连接服务器检查address和routes,是否可以从ConnectionPool获取一个连接如果没有获取到连接,会进行下一个路由选择(routeSelector),并且重新尝试从ConnectionPool获取一个连接。重试还是获取不到,就会重新创建一个连接(RealConnection)获取连接后,它会与服务器建立一个直接的Socket连接、使用TLS安全通道(基于HTTP代理的HTTPS),或直接TLS连接发送HTTP请求,并获取响应
ConnectInterceptor
在请求发送前的逻辑,都是ConnectInterceptor中实现,ConnectInterceptor的intercept,这个是3.14.2版本源码,和以前多版本稍微有些区别。
![344b41a36789789b008fbfaeb8540266.png](https://i-blog.csdnimg.cn/blog_migrate/40e222c7386316efb25e660054a4caa9.jpeg)
Exchange可以传输HTTP请求和响应,并管理连接和事件。newExchange方法调用:
![d76e878458d68fa6d9442338fe5bc628.png](https://i-blog.csdnimg.cn/blog_migrate/5ca0e6cf27149d565638d1c2d748e5c7.jpeg)
find方法会最终执行ExchangeFinder的findConnection方法,在发送HTTP请求之前的逻辑,都是这个方法中实现。
![c45bb254a59ade5869822a110d6d7c3a.png](https://i-blog.csdnimg.cn/blog_migrate/c8eafe5bf958b0e6743b8913afd915f9.jpeg)
HTTP 的连接主要是result.connect方法:
![64e5bf7b2fbf80911de65bac3f818087.png](https://i-blog.csdnimg.cn/blog_migrate/11e84dd099a8543f46621ea3bef6c087.jpeg)
在 for 循环中检查这个连接是否是隧道协议连接。connectSocket连接socket,establishProtocol根据HTTP协议版本进行连接处理。重点分析下connectSocket方法:
![62954ad63d421aea4cca4f7b08cc4e5b.png](https://i-blog.csdnimg.cn/blog_migrate/cc2f2b692ffd57f5a5df541a6c082210.jpeg)
使用 Okio,封装了Socket的读写操作, 建立连接后,就可以发送请求和获取响应。
CallServerInterceptor
CallServerInterceptor的intercept()方法里负责发送请求和获取响应。具体操作都是通过Exchange来执行,Exchange通过各个功能模块再进行分发处理。通过 Socket 发送 HTTP消息,会按照以下声明周期:
- writeRequestHeaders发送 request Headers
- 如果有 request body,就通过 Sink 发送request body,然后关闭 Sink
- readResponseHeaders获取 response Headers
- 通过Source读取 response body,然后关闭 Source
writeRequestHeaders
Exchange 调用writeRequestHeaders方法
![3f6560abf1b4fcf035d514db6f94842c.png](https://i-blog.csdnimg.cn/blog_migrate/c0c2fb8399ed7c9fe9eb4d8b6d2eb4aa.jpeg)
实际执行的方法codec实现类Http1ExchangeCodec(前面根据HTTP协议版本选择)的writeRequest方法
![90810d03d28787034a44d820a29d542a.png](https://i-blog.csdnimg.cn/blog_migrate/e59e1b8c13f94d4405702dcbe913470d.jpeg)
readResponseHeaders
读取响应头部,Http1ExchangeCodec的readResponseHeaders方法:
![26f0a68512cef75ec62e1bf3b2d4a9bc.png](https://i-blog.csdnimg.cn/blog_migrate/21f038c9d6bfa72d60357428c9ba224f.jpeg)
StatusLine解析HTTP版本信息,readHeaders()读取response header 信息。
![85f8fb176aca08d3b79d011ab6f3fdfc.png](https://i-blog.csdnimg.cn/blog_migrate/9305abc56518210c65c10f24d39c14a2.jpeg)
response body
解析 response body 内容:
![1909a4391babcd485c51360e25b71122.png](https://i-blog.csdnimg.cn/blog_migrate/994c977aaa7e19deea2cf88047bac3e5.jpeg)
如果不是websocket,调用Exchange的openResponseBody方法:
![c7a79a2e45d833e1bd07a9a41ad643ad.png](https://i-blog.csdnimg.cn/blog_migrate/ed50f35e3b6a2f37213eecb8099a9aa5.jpeg)
获取返回的 body,通过 Source 转换为需要的数据类型,ResponseBody提供的 string(),转换为 String 类型
![c2f31fe484f25d6d4f3a01520140710c.png](https://i-blog.csdnimg.cn/blog_migrate/8cf2670940ca452bbb55d898c9970728.jpeg)
通过上述的分析,OKHttp是通过Okio操作Socket实现了Http协议,凭借高效的性能,Android系统从4.4版本开始,HTTP的实现已经替换为OKHttp。
参考
- OKHttp源码解析(一)—初阶
- 拆轮子系列:拆 OkHttp