okhttp简述

这篇文章主要讲 Android 网络请求时所使用到的各个请求库的关系,以及 OkHttp3 的介绍。(如理解有误,请大家帮忙指出)
建议先对 HTTP 协议有一个了解,「ruanyifeng」大神文章
HTTP 协议入门
互联网协议入门(一)
互联网协议入门(二)
简介
开始一直不是很清楚各个 Android 网络库的本质区别,Android-async-http、HttpClient、HttpUrlConnection、Volley、OkHttp、Retrofit...等等,我们可以使用它们实现 Android 端的网络请求,但是它们却又与其他的库略有不同
我们知道,要想实现网络请求,就要发送我们的请求到服务器端,并接收响应。其中,上面所写的库中,HttpClient、HttpUrlConnection、OkHttp 是实现了 Http 协议的,可以帮助我们发送请求,接收响应,我们称之为 Http 客户端,而 Android-async-http、Volley、Retrofit 只是以这三个 Http 客户端为底层实现,进而封装的请求库,这就是它们的区别
Android Http 客户端
Http 客户端:可以发出请求并接收响应,实现了 HTTP 协议。至于怎么实现,大致就是通过数据流传递 Request 信息,并在接收到 Response 数据流后,进行解析。那数据流是怎么发送呢,请返回顶端,看「ruanyifeng」大神的文章,这里面涉及到的是 DNS、TCP/IP、MAC 地址等信息,不在本文探讨范围之内。
Android 相关的 Http 客户端发展到目前为止,以HttpClient
、HttpUrlConnection
、OkHttp
为主。
HttpClient:Apache 出品,Android 不建议使用,SDK 6.0 已经删除了相关类。

HttpUrlConnection:轻量极的 HTTP 客户端,API 简单,易扩展,不过从 Android4.4 开始 HttpURLConnection 的底层实现采用的是 OkHttp。

OkHttp:高性能的 http 库,支持同步、异步,而且实现了 spdy、http2、websocket 协议,api 简洁易用。

其实我们甚至可以自己来实现 HTTP 协议,写一个 HTTP 客户端。
Android 网络请求库
网络请求库是对 Android Http 客户端进行了进一步封装,当然,称其为网络请求库是为了与上面三个区分开来。网络请求库也可以称为 HTTP 客户端,它有客户端的所要求的功能,但是其底层使用的就是上面三个客户端其中的一种(基本上),虽然直接使用 Android Http 客户端请求网络亦无不可,不过封装之后则更加方便我们使用
Android-async-http:异步网络库,底层使用的是 HttpClient,但是 Android 不建议使用 HttpClient,因此,该库已不适合在 Android 开发中使用

Volley:异步网络库,在 Android 2.3 及以上版本,使用的是 HttpURLConnection,而在Android 2.2 及以下版本,使用的是 HttpClient。Btw,Volley 已经停止了更新。

Retrofit:一个 RESTful 的 HTTP 网络请求框架的封装。从 Retrofit 2.0 开始,内置 OkHttp,前者专注于接口的封装,后者专注于网络请求的高效

...

当然还有很多网络请求库,就不一一列举了。
OkHttp
OkHttp 优势
为什么说 OkHttp 高效呢?来看一下其官方介绍。
OkHttp 官方介绍
HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP efficiently makes your stuff load faster and saves bandwidth.
OkHttp is an HTTP client that’s efficient by default:
HTTP/2 support allows all requests to the same host to share a socket.
Connection pooling reduces request latency (if HTTP/2 isn’t available).
Transparent GZIP shrinks download sizes.
Response caching avoids the network completely for repeat requests.

OkHttp perseveres when the network is troublesome: it will silently recover from common connection problems. If your service has multiple IP addresses OkHttp will attempt alternate addresses if the first connect fails. This is necessary for IPv4+IPv6 and for services hosted in redundant data centers. OkHttp initiates new connections with modern TLS features (SNI, ALPN), and falls back to TLS 1.0 if the handshake fails.
Using OkHttp is easy. Its request/response API is designed with fluent builders and immutability. It supports both synchronous blocking calls and async calls with callbacks.
OkHttp supports Android 2.3 and above. For Java, the minimum requirement is 1.7.


OkHttp 是一个高效的 HTTP 客户端:
支持 HTTP/2 ,共享同一个Socket来处理同一个服务器的所有请求
如果 HTTP/2 不可用,则通过连接池来减少请求延时
无缝的支持 GZIP 来减少数据流量
缓存响应数据来减少重复的网络请求

OkHttp 会从很多常用的连接问题中自动恢复。如果你的服务器配置了多个 IP 地址,当第一个 IP 连接失败的时候,OkHttp 会自动尝试下一个 IP。OkHttp 还处理了代理服务器问题和 SSL 握手失败问题。
使用 OkHttp 无需重写程序中的网络代码。OkHttp 实现了几乎和 HttpURLConnection 一样的 API 。如果使用了 Apache HttpClient,OkHttp 也提供了一个对应的 okhttp-apache 模块。
OkHttp 支持 Android 2.3 以上版本,java 最低要求 1.7 版本
摘自:http://frodoking.github.io/2015/03/12/android-okhttp/
OkHttp 从调用看源码
简单调用:
OkHttpClient client = new OkHttpClient();String run(String url) throws IOException { Request request = new Request.Builder() .url(url) .build(); Response response = client.newCall(request).execute(); return response.body().string();}

看一下调用过程
创建OkHttpClient
对象
创建Request
对象
通过OkHttpClient
对象调用newCall()
方法,传入创建好的Request
对象
执行execute()
方法,得到Response
对象。

OkHttpClient:
/** * Factory for calls, which can be used to send HTTP requests and read their responses. * ... * OkHttp performs best when you create a single OkHttpClient instance * ... /public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory{.../* * Prepares the {@code request} to be executed at some point in the future. /@Override public Call newCall(Request request) { return new RealCall(this, request, false / for web socket /);}/* * Uses {@code request} to connect a new web socket. */@Override public WebSocket newWebSocket(Request request, WebSocketListener listener) { RealWebSocket webSocket = new RealWebSocket(request, listener, new Random()); webSocket.connect(this); return webSocket;}...}

我们可以看到 源码中OkHttpClient
实现了Call.Factory
,WebSocket.Factory
的接口,就是说,它可以新建一个Call
,也可以新建一个web socket
.
Factory for calls, which can be used to send HTTP requests and read their responses....

Call 的工厂类,Call 用来发送 Http 请求,并读取响应
OkHttp performs best when you create a single OkHttpClient instance

同时,它实现了Cloneable
借口,源码中 javadoc 里建议全局只有一个OkHttpClient
实例,如果我们使用clone()
方法得到一个新的实例,这个新实例的变化不会影响到原来的实例。
暂且不管OkHttpClient
中其他属性和方法,我们再来看看Request

Request:
通过 Builder 模式,可以设置请求的url
、请求类型method
、请求头域headers
、请求体body
、请求tag
、缓存要求cacheControl

新建Call
对象时,传入该对象,根据设置的属性发起请求。
再来看看Call

Call:
/** * A call is a request that has been prepared for execution. A call can be canceled. As this object * represents a single request/response pair (stream), it cannot be executed twice. */public interface Call extends Cloneable { // 返回创建 Call 时所传入的 request Request request(); // 执行请求(同步) Response execute() throws IOException; // 执行请求(异步) void enqueue(Callback responseCallback); // 取消请求 void cancel(); // 请求是否正在执行 boolean isExecuted(); // 请求是否取消 boolean isCanceled(); // 克隆 Call 实例 Call clone(); interface Factory { Call newCall(Request request); }}

通过Call
的execute()
或enqueue(responseCallback)
方法发起请求。
RealCall
是Call
的实现类,我们来看一下关于execute()
方法的实现。
@Override public Response execute() throws IOException { // 判断 execute() 方法是否已经被执行 synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } // 获取 Call 的堆栈信息,设置到 RetryAndFollowUpInterceptor 对象中,在里面创建 StreamAllocation 时用到 // RetryAndFollowUpInterceptor:请求失败重试和重定向拦截器 captureCallStackTrace(); try { // 将该请求放入 Dispatcher 中,会加入一个 Deque(双向队列) 中,最后由 Dispatcher 分派请求任务 client.dispatcher().executed(this); // 通过拦截器后,获取到 Response Response result = getResponseWithInterceptorChain(); if (result == null) throw new IOException("Canceled"); return result; } finally { client.dispatcher().finished(this); }}

名词解释:
拦截器:观察,修改以及可能短路的请求输出和响应请求的回来。通常情况下拦截器用来添加,移除或者转换请求或者回应的头部信息。
拦截器可以控制在请求前后做一些操作,如打印日志等。
Dispatcher:异步请求策略,可以设置每个主机最大请求数(默认为5),和最大并发请求数(默认是64)。
其实不论是同步还是异步请求,都会经过 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来完成。

摘自:http://frodoking.github.io/2015/03/12/android-okhttp/
继续往下看,调用了 getResponseWithInterceptorChain() 获得 response,这个方法中,添加了各种拦截器,并调用 proceed() 开始执行拦截器
Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. // 将所有的拦截器添加到 list 中,通过 Chain 的 chain.proceed(request) 方法开始执行拦截器 // 同时,所有的拦截器,除 CallServerInterceptor 外,都要调用 proceed() 方法,它是所有拦截器顺序调用的关键,下面会解释 List<Interceptor> interceptors = new ArrayList<>(); // 添加创建 OkHttpClient 时设置的自定义拦截器 interceptors.addAll(client.interceptors()); // 请求失败重试和重定向拦截器 interceptors.add(retryAndFollowUpInterceptor); // 补全缺失的一些http header。对后续Interceptor的执行的影响主要为修改了Request。 interceptors.add(new BridgeInterceptor(client.cookieJar())); // 处理http缓存。对后续Interceptor的执行的影响为,若缓存中有所需请求的响应,则后续Interceptor不再执行。 interceptors.add(new CacheInterceptor(client.internalCache())); // 借助于前面分配的StreamAllocation对象建立与服务器之间的连接,并选定交互所用的协议是HTTP 1.1还是HTTP 2。对后续Interceptor的执行的影响为,创建了HttpStream和connection。 interceptors.add(new ConnectInterceptor(client)); if (!forWebSocket) { // 添加网络拦截器(上面是应用拦截器) interceptors.addAll(client.networkInterceptors()); } // Interceptor链中的最后一个Interceptor,用于处理IO,与服务器进行数据交换(不调用 proceed() 方法的拦截器,完成之后返回 Response) interceptors.add(new CallServerInterceptor(forWebSocket)); // 生成 Chain 对象,调用 proceed() 方法,在拦截器的实现中,会新建 Chain 对象,同时也会调用 proceed() 方法,类似递归的形式 Interceptor.Chain chain = new RealInterceptorChain( interceptors, null, null, null, 0, originalRequest); return chain.proceed(originalRequest);}

我们再来看 Chain 对象中的 proceed() 方法
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection) throws IOException { ... // Call the next interceptor in the chain. // 咚咚咚 敲黑板,这边就是拦截器顺序调用的核心 // 新建一个 Chain 对象,获取下一个拦截器,调用拦截器的 intercept() 方法 // 在拦截器实现的该方法中会再次调用 proceed() 方法,直到最后一个拦截器返回 response RealInterceptorChain next = new RealInterceptorChain( interceptors, streamAllocation, httpCodec, connection, index + 1, request); Interceptor interceptor = interceptors.get(index); Response response = interceptor.intercept(next); ... return response;}

在最后的拦截器 CallServerInterceptor 中,将会请求服务器,返回 response。
以上是同步请求的过程,异步请求过程类似,不同点在于 Dispatcher 分派任务,异步请求使用 enqueue() 方法
@Override public void enqueue(Callback responseCallback) { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); // 添加任务到异步 Deque 中,会根据当前请求情况,决定是否当即进行请求 client.dispatcher().enqueue(new AsyncCall(responseCallback));}

Dispatcher.class
synchronized void enqueue(AsyncCall call) { // 判断请求数等是否达到上限 if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); // 通过 ExecutorService 执行请求 executorService().execute(call); } else { readyAsyncCalls.add(call); }}

RealCall.class(RealCall.AsyncCall)
final class AsyncCall extends NamedRunnable { ... // 异步请求执行后会调用到该方法 @Override protected void execute() { boolean signalledCallback = false; try { // 同样通过 getResponseWithInterceptorChain() 方法获取 response 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) { if (signalledCallback) { // Do not signal the callback twice! Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e); } else { responseCallback.onFailure(RealCall.this, e); } } finally { client.dispatcher().finished(this); } }}

以上就是 OkHttp 调用过程,比较简单,基本上就是调用 Call 执行方法,将请求添加到 Dispatcher,根据是否缓存从不同的源获取数据,又根据同步还是异步,分别执行 execute() 返回 response 或者是添加到请求队列,通过回调获取 response
了解调用过程后,我们再分析其他 OkHttp 特性就有了方向
如 OkHttp 的失败重连及重定向机制,可以从 RetryAndFollowUpInterceptor 入手,这个拦截器就是用来实现该功能的
如 缓存策略,我们可以从 CacheInterceptor 入手,其中用到了CacheStrategy
类,可以确定我们是用缓存数据、网络数据还是两个皆有,我们可以看这个类入手来了解 OkHttp 的缓存机制
等等

初研究 Android 网络请求 与 OkHttp,记与此。
参考:
http://www.jianshu.com/p/2fa728c8b366
OkHttp-Wiki
OkHttp-Wiki-译(简书)
http://frodoking.github.io/2015/03/12/android-okhttp/
http://www.jianshu.com/p/5c98999bc34f

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,394评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • Okhttp使用指南与源码分析 标签(空格分隔): Android 使用指南篇# 为什么使用okhttp### A...
    背影杀手不太冷阅读 8,140评论 2 119
  • 无法对你表白, 放任感情流逝好几年。 爱你说不出口, 卡在欲说还休的怨言中。 我无能为力, 你不知我对你孤单的思念...
    菁鹤堂_刘昭川鹤阅读 431评论 0 0
  • 有人说,失败是成功之母。我想,失败对于成功的作用自然不可或缺。但是,我个人认为一个人最大的成功是来源于他的自律,他...
    灰姑娘玻璃鞋阅读 696评论 0 1