okhttp原理

Android为我们提供了两种HTTP交互的方式:HttpURLConnection 和 Apache HTTP Client,虽然两者都支持HTTPS,流的上传和下载,配置超时,IPv6和连接池,已足够满足我们各种HTTP请求的需求。但更高效的使用HTTP 可以让您的应用运行更快、更节省流量。而OkHttp库就是为此而生。

OkHttp是一个高效的HTTP库:

支持 SPDY ,共享同一个Socket来处理同一个服务器的所有请求
如果SPDY不可用,则通过连接池来减少请求延时
无缝的支持GZIP来减少数据流量
缓存响应数据来减少重复的网络请求
会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,OkHttp会自动尝试下一个IP。OkHttp还处理了代理服务器问题和SSL握手失败问题。

使用 OkHttp 无需重写您程序中的网络代码。OkHttp实现了几乎和java.net.HttpURLConnection一样的API。如果您用了 Apache HttpClient,则OkHttp也提供了一个对应的okhttp-apache 模块。
总体设计


okhttp原理
文章图片
上 面是OKHttp总体设计图,主要是通过Dispatcher不断从RequestQueue中取出请求(Call),根据是否已缓存调用Cache或 Network这两类数据获取接口之一,从内存缓存或是服务器取得请求的数据。该引擎有同步和异步请求,同步请求通过Call.execute()直接返回当前的Response,而异步请求会把当前的请求Call.enqueue添加(AsyncCall)到请求队列中,并通过回调(Callback) 的方式来获取最后结果。

请求流程图

下面是关于OKHttp的请求流程图
okhttp原理
文章图片
从OkHttpClient类的整体设计来看,它采用门面模式来。client知晓子模块的所有配置以及提供需要的参数。client会将所有从客户端发来的请求委派到相应的子系统去。

在 该系统中,有多个子系统、类或者类的集合。例如上面的cache、连接以及连接池相关类的集合、网络配置相关类集合等等。每个子系统都可以被客户端直接调 用,或者被门面角色调用。子系统并不知道门面的存在,对于子系统而言,门面仅仅是另外一个客户端而已。同时,OkHttpClient可以看作是整个框架 的上下文。
通过类图,其实很明显反应了该框架的几大核心子系统;路由、连接协议、拦截器、代理、安全性认证、连接池以及网络适配。从client大大降低了开发者使用难度。同时非常明了的展示了该框架在所有需要的配置以及获取结果的方式。
在接下来的几个Section中将会结合子模块核心类的设计,从该框架的整体特性上来分析这些模块是如何实现各自功能。以及各个模块之间是如何相互配合来完成客户端各种复杂请求。
同步与异步的实现

在发起请求时,整个框架主要通过Call来封装每一次的请求。同时Call持有OkHttpClient和一份HttpEngine。而每一次的同步或者异步请求都会有Dispatcher的参与,不同的是:
同步
Dispatcher会在同步执行任务队列中记录当前被执行过得任务Call,同时在当前线程中去执行Call的getResponseWithInterceptorChain()方法,直接获取当前的返回数据Response;
异步
首先来说一下Dispatcher,Dispatcher内部实现了懒加载无边界限制的线程池方式,同时该线程池采用了 SynchronousQueue这种阻塞队列。SynchronousQueue每个插入操作必须等待另一个线程的移除操作,同样任何一个移除操作都等 待另一个线程的插入操作。因此此队列内部其 实没有任何一个元素,或者说容量是0,严格说并不是一种容器。由于队列没有容量,因此不能调用peek操作,因为只有移除元素时才有元素。显然这是一种快 速传递元素的方式,也就是说在这种情况下元素总是以最快的方式从插入者(生产者)传递给移除者(消费者),这在多任务队列中是最快处理任务的方式。对于高 频繁请求的场景,无疑是最适合的。
异步执行是通过Call.enqueue(Callback responseCallback)来执行,在Dispatcher中添加一个封装了Callback的Call的匿名内部类Runnable来执行当前 的Call。这里一定要注意的地方这个AsyncCall是Call的匿名内部类。AsyncCall的execute方法仍然会回调到Call的 getResponseWithInterceptorChain方法来完成请求,同时将返回数据或者状态通过Callback来完成。
接下来继续讲讲Call的getResponseWithInterceptorChain()方法,这里边重点说一下拦截器链条的实现以及作用。
拦截器有什么作用

先来看看Interceptor本身的文档解释:观察,修改以及可能短路的请求输出和响应请求的回来。通常情况下拦截器用来添加,移除或者转换请求或者回应的头部信息。
拦截器接口中有intercept(Chain chain)方法,同时返回Response。所谓拦截器更像是AOP设计的一种实现。下面来看一个okhttp源码中的一个引导例子来说明拦截器的作用。
缓存策略

在OkHttpClient内部暴露了有Cache和InternalCache。 反馈回来的数据重点看一下缓存日志。第一次是来至网络数据,第二次来至缓存。
sendRequest()

此方法是对可能的Response资源进行一个预判,如果需要就会开启一个socket来获取资源。如果请求存在那么就会为当前request添加请求头部并且准备开始写入request body。
readResponse()

此方法发起刷新请求头部和请求体,解析HTTP回应头部,并且如果HTTP回应体存在的话就开始读取当前回应头。在这里有发起返回存入缓存系统,也有返回和缓存系统进行一个对比的过程。
安全性

连接安全性主要是在HttpEngine.connect()方法。上一节油讲到地址相关的选择,在HttpEngine中有一个静态方法 createAddress(client, networkRequest),在这里通过获取到OkHttpClient中关于SSLSocketFactory、HostnameVerifier 和CertificatePinner的配置信息。而这些信息大部分采用默认情况。这些信息都会在后面的重连中作为对比参考项。
同时在Connection.upgradeToTls()方法中,有对SSLSocket、SSLSocketFactory的创建活动。这些创 建都会被记录到ConnectionSpec中,当发起ConnectionSpec.apply()会发起一些列的配置以及验证。
建议有兴趣的同学先了解java的SSLSocket相关的开发再来了解本框架中的安全性,会更能理解一些。
平台适应性
讲了很多,终于来到了平台适应性了。Platform是整个平台适应的核心类。同时它封装了针对不同平台的三个平台类Android和JdkWithJettyBootPlatform。
【okhttp原理】代码实现在Platform.findPlatform中

    推荐阅读