Android网络平台的三板斧基本被square公司承包了,Okhttp,Retrofit,Okio真但是三巨头。平时用的okhttp比较多,因此咱们颇有必要来看看它的实现原理。java
相信你们对okhttp的使用一点都不陌生了,我这里不会详细讲解它的使用,仅仅把官网上的搬下来看看,做为研究的入口:web
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
复制代码
这个是比较简单的Get请求json
public static final MediaType JSON
= MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
复制代码
这个是Post请求设计模式
从上面的使用来看,不论是get仍是post请求,都会先建立OkHttpClient实例,这个很容易理解,没有实例哪来的入口呢,观摩一下:缓存
OkHttpClient client = new OkHttpClient();
复制代码
接着看看实例化了哪些东西bash
public OkHttpClient() {
this(new Builder());
}
复制代码
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
...
}
复制代码
这是默认的实例化方式,builder里面也都是默认值,主要实例了代理,缓存,超时等等一些参数,这些系统都给咱们设置了一些默认的值,好比超时是10s,读写也是10s等等。可是在实际状况中,咱们每每去要自定义一些方式,好比,读写时间设置30s,增长一些拦截器等,这就须要另一种实例化的方式了。服务器
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.connectTimeout(20 * 1000, TimeUnit.MILLISECONDS)
.readTimeout(30 * 1000, TimeUnit.MILLISECONDS)
/* 关闭OkHttp失败重试链接的机制,该问题致使发帖重复的问题 */
.retryOnConnectionFailure(false)
.addInterceptor(new EnhancedCacheInterceptor())
.addInterceptor(new SecurityInterceptor(mApplicationContext)) // 加密解密
.addNetworkInterceptor(new StethoInterceptor())
.dns(new dsn().build();
复制代码
看上去很清晰,采用的是建造者模式,咱们设置自定义参数,而后经过build去实例化。这两种方式具体怎么用,选择哪种那就看咱们具体业务了。cookie
接下来看看Request的封装网络
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
复制代码
一样的,无论什么请求方式,都要构建Requestapp
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Object tag;
private volatile CacheControl cacheControl; // Lazily initialized.
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
复制代码
能够看到request包含url,请求方法,header,以及body。这也就对应了http里面的请求头部,包含一些具体信息。它也是采用的建造者模式去构造的。平时咱们用得比较多的是post请求,这里咱们作个简要的分析:
RequestBody body = RequestBody.create(JSON, json);
...
public Builder post(RequestBody body) {
return method("POST", body);
}
复制代码
首先咱们构造RequestBody,body里面封装表单或者json,而后调用post方法:
if (method == null) throw new NullPointerException("method == null");
if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
if (body != null && !HttpMethod.permitsRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must not have a request body.");
}
if (body == null && HttpMethod.requiresRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must have a request body.");
}
this.method = method;
this.body = body;
return this;
}
复制代码
这里面很简单,分别对method,body作个校验,这二者都不能为空,也就是说,当采用post去发请求的时候body是不能为空的,这个错误有时候咱们常常会遇到。而后赋值给request的变量存着备用。
ok,若是上面的都是准备工做,那么接下来就是正式发送请求了。仍是直接看代码:
Response response = client.newCall(request).execute()
复制代码
这个是发送同步请求的,哇塞,一句话?是的,就是一句话解决了,看上去简直难以想象,咱们看看源码newCall方法,很显然call是发送请求的核心:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
复制代码
接着去RealCall类中去调用newRealCall方法:
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
复制代码
构造函数把参数赋值,而后new了一个实例RetryAndFollowUpInterceptor,这个咱们后面再来看。接着就是调用execut函数了:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
复制代码
这里作了几件事:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
复制代码
这看上去有点儿陌生,里面增长了各类拦截器,而后调用了chain.proceed(originalRequest),感受这才是大boss啊,官网里面有句话
the whole thing is just a stack of built-in interceptors.
复制代码
这句话颇有含金量,也就是说okhttp所作的是就是拦截各类请求去解析,对拦截器一一作处理,纳闷了,为啥要拦截这么多?由于一个请求的发送到接收到返回数据,中间要作不少是事,好比失败重传,缓存,还有自定义的拦截器,好比对数据加密等等。
首先给出一张总体图:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null. if (response == null) { throw new NullPointerException("interceptor " + interceptor + " returned null"); } if (response.body() == null) { throw new IllegalStateException( "interceptor " + interceptor + " returned a response with no body"); } return response; } 复制代码
这是核心中的核心,它采用了责任链模式,这种模式,是java设计模式中比较重要的一种,你们不懂的能够单独去查查,它的整体思想就是若是本身能够消费事件就消费,不消费就传递给下一个拦截器。
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
复制代码
能够看到这里的index进行了+1处理,而后开始启动下一个拦截器。很显然,这这些拦截器的执行是有顺序的。咱们先看看这种拦截器的做用:
/**
* Bridges from application code to network code. First it builds a network request from a user
* request. Then it proceeds to call the network. Finally it builds a user response from the network
* response.
*/
复制代码
它的意思是把用户的代码,也就是咱们写的代码,转换为服务器标准的代码。解释:它会提取http请求所须要的一些参数,好比agent,heades,contenttype等,而后发送出去。响应的过程也是同样,把服务器的响应结果转换为咱们须要的,好比把respones转换为咱们须要的对象。实质就是按照http协议标准化格式。
/** Serves requests from the cache and writes responses to the cache. */
复制代码
这个主要是处理缓存的,咱们发送请求和获取响应结果的时候,不能每次都去从新建立吧,那样效率过低,首先去从缓存中去取没,若是缓存中有现有的链接,咱们直接发请求,不用重复建立。响应结果也是同样的。
/** Opens a connection to the target server and proceeds to the next interceptor. */
复制代码
若是缓存没有命中,那么就去新建链接啦,这个用来新建链接的。
/** This is the last interceptor in the chain. It makes a network call to the server. */
复制代码
这个是责任链模式的尾端,它最终会去经过网络请求服务器资源。
这里我来着重分析一下ConnectInterceptor,CallServerInterceptor这两种,由于这是最终创建链接和发送请求的过程,能够说是最重要的。
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
复制代码
这个过程建立了HttpCodec,可是它并无在这个拦截器中去使用,而是传递到后面发动请求的拦截器中去了也便是CallServerInterceptor。HttpCodec究竟是什么,它是对HTTP协议作了抽象处理,在 Http1Codec 中,它利用 Okio 对 Socket 的读写操做进行封装,Okio 之后有机会再进行分析,如今让咱们对它们保持一个简单地认识:它对 java.io 和 java.nio 进行了封装,让咱们更便捷高效的进行 IO 操做。
@Override public Response intercept(Chain chain) throws IOException {
HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream();
StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation();
Request request = chain.request();
long sentRequestMillis = System.currentTimeMillis();
httpCodec.writeRequestHeaders(request);
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
httpCodec.finishRequest();
Response response = httpCodec.readResponseHeaders()
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
if (!forWebSocket || response.code() != 101) {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
// 省略部分检查代码
return response;
}
复制代码
这个里面代码比较长,咱们只保留核心分心,上面不是传递了HttpCodec了嘛,这里就利用起来了,它是利用okio进行发送请求,这里okio不是咱们分析的重点,读者能够单独找文章去看看。 总之:Okhttp发送请求是经过okio去作的,okio实质上就是对socket进行了封装,一层套一层而已,这就是神秘的面纱后面的简单。 这里作了几件事:
咱们对okhttp作了个总体介绍,固然里面有不少细节没有解释,也不想去解释,读者有须要本身去研究。这里我总结一下:
OKhttp中使用的设计模式:建造者模式和责任链模式;
最后上一张图片,这个是从其余读者文章弄过来的,很用心,我就直接使用了;