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的官网为:okhttp

重要类和结构

okhttp module

ConnectionSpec

指明了连接规范,包括信息有:是否tls,加密算法,tls协议版本,是否支持tls扩展等

OkHostnameVerifier

实现HostnameVerifier接口,提供验证服务器证书和host的功能

CertificatePinner

使用该类可以指定服务器端的证书必须包含的信息,如果没有,则会服务器证书验证失败

Authenticator

当服务器或者代理服务器需要验证客户端身份时,该类可以可以通过authenticate回调生成一个带有权限request进行验证,或者返回一个null不进行继续验证

ConnectionPool

该类用于重用HTTP连接已达到减少延迟的目的,同一个http地址可以共享一个Connection。

  • connections:Deque
    用于管理connection列表
  • connectionBecameIdle
    当connection变成idle时,会触发ConnectionPool的该方法。如果明确变成idle的connect标记noNewStream时,或者用户配置maxIdleConnections<=0,则直接从connection队列中移除该connection,否则通过notifyAll的方式触发cleanupRunnable进行clean操作。
  • cleanupRunnable
    该runnable用于根据特定的条件自动清除idle的connection。每当清除之后,会自动wait进行睡眠等待超时唤醒或者notify唤醒

OkHttpClient

该类主要用于记录用户关于网络需求的各项配置

  • Builder:
    用于配置初始化OKHttpClient所需参数,其中主要包括,protocols,connectionSpecs,hostNameVerifier, certificatePinner,cookieJar, Cache 等等
  • static域中对Internal的instance进行了初始化

Request

该类用于生成一个网络请求,包含最基本的请求信息,url,method,headers,body,tag, cachecontrol。内部提供builder类用于生成Request。Request使用HttpUrl,Headers,CacheControl等工具类,方便解析和管理url和header,缓存机制

Response

和Request对应的类是Response。包含的基本信息有:request,protocol,code,message,handshake,body,networkResponse,cacheResponse,priorResponse

Dispatcher

  • ExecutorService
    线程管理池,用于执行各个队列中的call
  • runningSyncCalls:Deque
    同步的RealCall会被push到该队列中
  • runningAsyncCalls,readyAsyncCalls:Deque
    异步的asyncCall会被push到runningAsyncCalls中,如果超过最大并行call的限制,会先放入readyAsyncCalls中进行存储,在慢慢往runningAsyncCalls移动

RealCall implements Call

当准备好Request时,可以通过OkHttpClient的newCall产生一个RealCall,来准备网络请求的发起。该类实现了Call接口,主要有同步请求execute,异步请求enqueue,取消cancel等操作

  • excute()
    同步执行网络请求,中间非常有意思的是使用了CloseGuard监听respond.body.close(CloseGuard。接着使用okhttpclient的dispatcher进行执行记录处理情况。真正执行请求是调用getResponseWithInterceptorChain方法中
  • enqueue(Callback)
    异步执行网络请求,通过内部类AsyncCall将自己进行包装成NamedRunnable,通过okhttpclient的dispatcher进行线程调用。最终网络请求发生在AsyncCall的execute中通过调用getResponseWithInterceptorChain进行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //这里贴出getResponseWithInterceptorChain的核心代码
    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);
    return chain.proceed(originalRequest);
    }

Interceptor

这个接口扮演着一个非常重要的角色,任何一个请求可以被一个或者多个Interceptor组成的链表进行依次处理,用户可以设计自己的Interceptor对一个请求的生命周期内进行自定义的处理,下面给出Interceptor的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Observes, modifies, and potentially short-circuits requests going out and the corresponding
* responses coming back in. Typically interceptors add, remove, or transform headers on the request
* or response.
*/
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable Connection connection();
}
}

RealInterceptorChain

之所以Interceptor能按照依次处理,主要依赖于RealInterceptorChain的process实现,具体代码如下所示,在process的过程中,增加index,生成新的RealInterceptorChain,调用对应interceptor的intercept方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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);
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");
}
return response;
}

Route

Route主要包含3个主要信息:Address,proxy,inetSocketAddress

RouteSelector

Route的选择器,该选择器通过Address和RouteDataBase进行初始化。RouteDataBase用于记录失败Route

  • proxies/nextProxyIndex
    用于记录对应的proxy列表,nextProxyIndex用于记录当前proxy指针
    每次初始话时,根据address信息初始化Proxy列表和指针

  • inetSocketAddresses/nextInetSocketAddressIndex
    用于记录对应的socket地址列表,nextInetSocketAddressIndex用于记录当前指针
    每次更替Proxy时,需要重新reset inetSocketAddresses列表和指针

  • postponedRoutes
    用于记录需要推迟失败的route

StreamAllocation

用于处理Connection,Stream,Calls之间的关系。其中Connection代表和服务器链接的socket。Stream代表一次请求对(request/respond),而Call代表一组Stream,涉及到最初的request以及承接的request

  • release
    主要工作为:将自身从绑定的RealConnection.allocations中移除,如果RealConnection.allocations空了,需要做对应的socket关闭

  • steamFailed
    在StreamAllocation中分析错误类型,判断是否还能在同一个connection上重试,如果不行,则标记noNewStream为true,并且deallocate connection,在connection没有allocations时,适当的释放掉socket

  • newStream
    通过findHealthyConnection找到一个connection,寻找connection的过程中,先复用steamAllocation中存在的connection,或者去connectionPool中寻找,最后如果实在不行在生成一个新的connection,生成之后调用connection的connect方法
    最后通过connection的newCodec生成一个HttpCodec

RetryAndFollowUpInterceptor

该类interceptor用于重试,以及follow redirect url。StreamAllocation在该interceptor中进行创建,并且传递给后面的interceptor。该interceptor最主要的处理在获取response过程中如果发生exception,则尝试recover,如果有follow的request,则继续发起请求,下面给出了核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
int followUpCount = 0;
Response priorResponse = null;
while (true) {
......
Response response = null;
boolean releaseConnection = true;
try {
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
Request followUp = followUpRequest(response);
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
......
request = followUp;
priorResponse = response;
}
}

BridgeInterceptor

用于连接应用层和网络层,其中在OkHttpClient中配置的cookiejar会在该类中进行处理。

  • intercept
    在intercept过程中,给reqeust添加上”Content-Type”,”Content-Length”,”Host”,”Accept-Encoding”,”Cookie”等headers
    同时在收到respond时,将cookie保存到CookieJar中。如果收到body经过了gzip的压缩,则自动解压

CacheInterceptor

中间通过InternalCache结构来控制网络respond缓存。http的缓存策略就是在这个interceptor中进行控制的

ConnectInterceptor

httpCodec和RealConnection是在这个interceptor中建立的,然后传递到chain的处理过程之中
httpCodec和connection最终通过streamAllocation的newStream进行创建,如果是TLS,还需要在这一步中和服务器进行握手

CallServerInterceptor

该Interceptor作为处理链中最后一层,主要任务即为通过httpCodec将request发送到服务器端,同时生成response

学习知识点

SSL/TLS简介

SSL/TLS协议简介

TLS握手简介

MD5,SHA1

MD5,SHA1比较

AES,SHA1,DES,RSA,MD5区别

AES,SHA1,DES,RSA,MD5区别

CloseGuard

okhttp库在RealCall的实现中,使用了CloseGuard来检查respond.body().close()方法是否显示被调用,具体CloseGuard的用法可以参考CloseGuard

第三方类库

  1. guava
    Guava is a set of core libraries that includes new collection types (such as multimap and multiset), immutable collections, a graph library, functional types, an in-memory cache, and APIs/utilities for concurrency, I/O, hashing, primitives, reflection, string processing, and much more!