这里不详细讲解如何使用,若是须要详细了解使用,请参考个人另外一篇文章Android OkHttp3.0 基本使用 html
咱们看一下基本使用java
HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(new HttpLogger());
logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
mOkHttpClient = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.addNetworkInterceptor(logInterceptor)
.build();
Request request = new Request.Builder()
.url(url)
.get()
.build();
Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
复制代码
下面咱们慢慢分析web
首先咱们经过Builder构建了一个OkHttpClient,Builder是OkHttpClient的一个内部类,其实就是一个Build模式,咱们看下Builder类是干什么的设计模式
public static final class Builder {
Dispatcher dispatcher;
Proxy proxy;
List<Protocol> protocols;
List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors = new ArrayList<>();
final List<Interceptor> networkInterceptors = new ArrayList<>();
EventListener.Factory eventListenerFactory;
ProxySelector proxySelector;
CookieJar cookieJar;
Cache cache;
InternalCache internalCache;
SocketFactory socketFactory;
SSLSocketFactory sslSocketFactory;
CertificateChainCleaner certificateChainCleaner;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
ConnectionPool connectionPool;
Dns dns;
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int connectTimeout;
int readTimeout;
int writeTimeout;
int pingInterval;
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
public OkHttpClient build() {
return new OkHttpClient(this);
}
复制代码
Builder
其实就是一个初始化参数的载体,好比你设置的超时时间,拦截器等,而后经过build
方法把this
赋值给OkHttpClient
,也就是把这些初始化的参数交给了OkHttpClient
缓存
而后构建一个Request服务器
public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final 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
也是经过内部类Builder
构建的,这也是Build
模式,他的做用也是一个参数的载体,好比url,method,headers,body
等cookie
最后经过Call call = mOkHttpClient.newCall(request)
; 获取Call
对象网络
** OkHttpClient.java
@Override
public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
复制代码
内部是建立了一个RealCall
对象,咱们看一下RealCall
就是实际发出请求的对象,他有俩个方法一个是同步请求,一个异步请求异步
同步请求socket
try {
Response execute = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
复制代码
咱们看一下源码
** RealCall.java
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
复制代码
这里作了四件事
client.dispatcher().executed(this)
方法,在同步中,其实这个方法并无太大做用,只是把Call加入到了一个集合中,而后方便以后的取消,和获取正在运行的CallResponse result = getResponseWithInterceptorChain();
获取Http返回的结果,这个是真正进行请求的方法client.dispatcher().finished(this);
方法通知Call已经执行完毕异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
复制代码
咱们看一下异步请求的源码
**RealCall.java
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
复制代码
client.dispatcher().enqueue(new AsyncCall(responseCallback));
去执行请求咱们先看一下AsyncCall
final class AsyncCall extends NamedRunnable {
...
@Override protected void execute() {
...
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
...
}
}
public abstract class NamedRunnable implements Runnable {
protected final String name;
...
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
复制代码
咱们能够看到AsyncCall
其实就是一个Runnable
,当执行这个Runnable
的时候对调用execute
方法,而execute
方法中仍是调用了Response response = getResponseWithInterceptorChain();
来进行真正的请求
咱们再来分析一下dispatcher
public final class Dispatcher {
//最大的请求数
private int maxRequests = 64;
//每台主机的最大请求数
private int maxRequestsPerHost = 5;
private Runnable idleCallback;
//线程池
private ExecutorService executorService;
//准备运行的异步Call
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//正在运行的异步Call
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//正在运行的同步Call
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
//异步
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
//同步
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
复制代码
咱们能够看到他其实维护了一个线程池
,和一些存储请求的队列
,同步请求是是直接把Call
加入到队列中,异步请求是,看当前正在请求的数量来判断加到那个队列中,若是当前运行的请求,小于最大请求数,就用线程池运行这个Call
最终咱们发现,无论是同步请求仍是异步请求,最终都是调用了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
是OkHttp
中很重要的一部分,采用了责任链的设计模式,他把网络请求,缓存,重连,等工能统一了起来,每个功能都是一个Interceptor
,他们在经过Interceptor.Chain
环环相扣,最终完成一次网络请求
咱们看一下他们究竟是怎么环环相扣的
首先上方,把全部的拦截器放入到了一个集合中,构建了第一个拦截器RealInterceptorChain
,看他内部作了什么
public final class RealInterceptorChain implements Interceptor.Chain .... public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {
...
// Call the next interceptor in the chain.
//构建下一个RealInterceptorChain,注意参数 index + 1
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
//获取第一个拦截器,并调用第一个拦截器的intercept方法
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
...
return response;
}
复制代码
上方是删减代码,咱们看一下,从新构建RealInterceptor
,注意参数index + 1
,而后获取第一个拦截器,而后调用了interceptor.intercept
方法,咱们看一下其余的interceptor的intercept方法作了什么,第一个拦截器就是RetryAndFollowUpInterceptor
** RetryAndFollowUpInterceptor.java
@Override public Response intercept(Chain chain) throws IOException {
...
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
releaseConnection = false;
...
}
复制代码
这是最关键的代码,他除了走本身的逻辑以外,他还调用 ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
,chain
是上方传入的next
,而后index+1,会让他执行下一个拦截器,如此循环
大概介绍一下每个拦截器具体作了什么
client.interceptors()
用户自定义的拦截器RetryAndFollowUpInterceptor
负责重连和重定向BridgeInterceptor
负责把用户的请求转换为发送到服务器的请求,把服务器的响应转换为用户友好的响应CacheInterceptor
负责缓存管理ConnectInterceptor
负责和服务器创建链接client.networkInterceptors
用户自定义拦截器,仅在网络请求时有用CallServerInterceptor
负责向服务器发送请求,从服务器读取响应咱们今天只分析CacheInterceptor
和ConnectInterceptor
@Override
public Response intercept(Chain chain) throws IOException {
//根据Request获取缓存的Response
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
//根据传入参数,判断缓存的策略,是否使用了网络或者缓存,或者俩者都使用
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
...
//请求不使用网络也不使用缓存,则直接返回code=504
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// 若是不须要使用网络,则直接把缓存返回
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
//交给下一级去请求网络
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
//若是服务器返回的是304则 返回缓存结果
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
//若是不是304,须要缓存,就把请求结果放入缓存中
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// 更新缓存
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
复制代码
DiskLruCache
这个拦截器只是打开了一个连接
@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, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
复制代码
这段代码很短,主要工做是,建立了一个HttpCodec
和RealConnection
,HttpCodec
的主要做用是编码请求和解码响应,RealConnection
是用来向服务器发起连接,这里面最重要就是获取连接的时候用到了链接池ConnectionPool
来复用连接
在streamAllocation.newStream
方法中,通过一系列调用,最终会调用到StreamAllocation
的findConnection
方法
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled) throws IOException {
Route selectedRoute;
synchronized (connectionPool) {
...
// Attempt to use an already-allocated connection.
//尝试用已分配的连接,若是连接容许建立新的流则返回连接,经过noNewStreams来判断是否容许建立新的流
RealConnection allocatedConnection = this.connection;
if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
return allocatedConnection;
}
// Attempt to get a connection from the pool.
//尝试从连接池中获取一个可用的连接
Internal.instance.get(connectionPool, address, this, null);
if (connection != null) {
return connection;
}
selectedRoute = route;
}
// If we need a route, make one. This is a blocking operation.
if (selectedRoute == null) {
selectedRoute = routeSelector.next();
}
RealConnection result;
synchronized (connectionPool) {
if (canceled) throw new IOException("Canceled");
// Now that we have an IP address, make another attempt at getting a connection from the pool.
// This could match due to connection coalescing.
//再次尝试在链接池中获取
Internal.instance.get(connectionPool, address, this, selectedRoute);
if (connection != null) return connection;
// Create a connection and assign it to this allocation immediately. This makes it possible
// for an asynchronous cancel() to interrupt the handshake we're about to do.
route = selectedRoute;
refusedStreamCount = 0;
//从新创建一个连接
result = new RealConnection(connectionPool, selectedRoute);
acquire(result);
}
// Do TCP + TLS handshakes. This is a blocking operation.
//这里进行TCP和TLS的握手
result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
routeDatabase().connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
// Pool the connection.
//把新建的连接放入链接池
Internal.instance.put(connectionPool, result);
// If another multiplexed connection to the same address was created concurrently, then
// release this connection and acquire that one.
if (result.isMultiplexed()) {
socket = Internal.instance.deduplicate(connectionPool, address, this);
result = connection;
}
}
closeQuietly(socket);
return result;
}
复制代码
上方的逻辑仍是很清楚的
public final class ConnectionPool {
private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize */,
Integer.MAX_VALUE /* maximumPoolSize */, 60L /* keepAliveTime */, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool", true));
/** The maximum number of idle connections for each address. */
private final int maxIdleConnections;
private final long keepAliveDurationNs;
//后台线程用于清理过时连接
private final Runnable cleanupRunnable = new Runnable() {
@Override public void run() {
while (true) {
long waitNanos = cleanup(System.nanoTime());
if (waitNanos == -1) return;
if (waitNanos > 0) {
long waitMillis = waitNanos / 1000000L;
waitNanos -= (waitMillis * 1000000L);
synchronized (ConnectionPool.this) {
try {
ConnectionPool.this.wait(waitMillis, (int) waitNanos);
} catch (InterruptedException ignored) {
}
}
}
}
}
};
//存储连接的队列
private final Deque<RealConnection> connections = new ArrayDeque<>();
final RouteDatabase routeDatabase = new RouteDatabase();
boolean cleanupRunning;
//取出连接
RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {
assert (Thread.holdsLock(this));
for (RealConnection connection : connections) {
if (connection.isEligible(address, route)) {
streamAllocation.acquire(connection);
return connection;
}
}
return null;
}
//放入连接
void put(RealConnection connection) {
assert (Thread.holdsLock(this));
if (!cleanupRunning) {
cleanupRunning = true;
executor.execute(cleanupRunnable);
}
connections.add(connection);
}
复制代码
其实链接池是维护一个队列储存连接,每次放入连接还会触发后台线程清除失效连接,每次取出都会使用connection.isEligible
校验该连接是否能够复用,其实链接池就是省去了创建链接和握手时间
若是链接池没有合适连接就会从新建立连接并握手, result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
咱们看一下这个方法到底作了什么
public void connect( int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled) {
....
while (true) {
try {
if (route.requiresTunnel()) {
connectTunnel(connectTimeout, readTimeout, writeTimeout);
} else {
connectSocket(connectTimeout, readTimeout);
}
//跟TLS握手
establishProtocol(connectionSpecSelector);
break;
...
}
private void connectSocket(int connectTimeout, int readTimeout) throws IOException {
Proxy proxy = route.proxy();
Address address = route.address();
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);
rawSocket.setSoTimeout(readTimeout);
try {
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
ce.initCause(e);
throw ce;
}
source = Okio.buffer(Okio.source(rawSocket));
sink = Okio.buffer(Okio.sink(rawSocket));
}
public void connectSocket(Socket socket, InetSocketAddress address, int connectTimeout) throws IOException {
socket.connect(address, connectTimeout);
}
复制代码
它内部分为是否须要隧道,不须要创建隧道则,则调用connectSocket
方法,最终是调用了socket.connect
,创建完socket连接以后,须要进行TLS握手,TLS我就不介绍了,我也不熟悉,参考SSL/TLS 握手过程详解