本文章基于OKHttp3.12.0分析
compile 'com.squareup.okhttp3:okhttp:3.12.0'
首先看我们调用OKHttp执行网络的一般代码:
private static void initOKHttp() {
client = new OkHttpClient.Builder().build();
}
private static void OKGetWeather() {
Request request = new Request.Builder()
.url("https://restapi.amap.com/v3/weather/weatherInfo?key=5326a9f59587393b549f3cffefa0459b&city=110000&output=json&extensions=base")
.tag("")
.build();
client.newCall(request).enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) { }
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {}
});
}
调用的时候是一个简洁明了的Builder模式,使用builder模式来against参数特别多的情况。简单的网络调用就这么几步
1. 构造OKHttpClient
2. 构造Request
3. 得到一个Call
4. 同步或异步执行Call
一步步来,我们先看OKHttpClient的代码,这里搬运一下OKHttpClient这个类的注释。包括一些简单的对OKHttpClient的介绍。
OKHttpClient是用来发送Http请求和解析Http回应的。 OKHttpClient应该是共享的。它的正确用法应该是创建一个单例并且为所有的Http请求所用。因为每个Client都会维持自己的连接池和线程池。重用连接和线程为了降低时延并且节省内存。所以,为每个Request创建一个Client会浪费资源,因为池子总是在空闲状态。
使用new OkHttpClient()去创建使用默认设置的一个共享的实例
// The singleton HTTP client.
public final OkHttpClient client = new OkHttpClient();
或者使用new OkHttpClient.Builder()去创建一个使用自定义设置的实例
// The singleton HTTP client.
public final OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor())
.cache(new Cache(cacheDir, cacheSize))
.build();
你还可以用newBuilder()定制一个共享的OkHttpClient实例,这样创建的Client会共享相同的连接池、线程池和配置使用builder方法去配置 衍生的client或者特殊的意图。
这个例子展示了一个使用了500ms超时时间的call
OkHttpClient eagerClient = client.newBuilder()
.readTimeout(500, TimeUnit.MILLISECONDS)
.build();
Response response = eagerClient.newCall(request).execute();
Shutdown不是必要的,Client持有的线程和连接如果一直空闲会自动被释放。但是如果你的application中需要强制释放掉不需要的资源你可以这样做
client.dispatcher().executorService().shutdown();
使用evictAll()清空连接池,注意连接池进程可能不会立刻退出
client.connectionPool().evictAll();
我们去看OKHttpClient的源码,其实就是一个大的入口类,其中静态内部类Builder的源码占了3/5的样子。都是关于其中属性的Builder方法调用,参数太多了,我们删减一下看
public Builder newBuilder() {
return new Builder(this);
}
public static final class Builder {
Dispatcher dispatcher;
@Nullable Proxy proxy;
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
...
readTimeout = 10_000;
}
Builder(OkHttpClient okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
...
}
public Builder connectTimeout(long timeout, TimeUnit unit) {
connectTimeout = checkDuration("timeout", timeout, unit);
return this;
}
public Builder readTimeout(long timeout, TimeUnit unit) {
readTimeout = checkDuration("timeout", timeout, unit);
return this;
}
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
我们调用builder的这些方法的时候会把我们的配置存储到builder里面,然后build的时候会把这些属性赋值到创建出来的OKHttpClient里面。
这里我们也看到了newBuilder()方法。上面的介绍中说如果想定制某一个属性但是还想共享OKhttpClient的其他属性的话可以用这个方法。我们看到用newBuilder的时候会把之前的OKHttpClient的方法再复制给builder,然后加上你自己修改的builder的方法重新new一个OKHttpClient出来,当然OKHttpClient只是算个书包,平时我们用书包背着工作生活用的电脑手机钱包,newBuider new出来的OKHttpClient算是我们换了个书包,但真正我们生活工作的电脑钱包并没有变,所以还会共享之前的配置。
OKHttpClient Call的入口是这里
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
这个方法会返回一个RealCall,我们看RealCall
final class RealCall implements Call {
final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
final AsyncTimeout timeout;
private @Nullable EventListener eventListener;
...
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
//实现Timeout,如果超过了时间就cancel掉
this.timeout = (AsyncTimeout) timeOut() -> { cancel(); };
//设置了给定的timeout时间
this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
}
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
@Override public Response execute() throws IOException {
...
timeout.enter();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
...
} finally {
client.dispatcher().finished(this);
}
}
@Override public void enqueue(Callback responseCallback) {
...
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
final class AsyncCall extends NamedRunnable {
}
RealCall实现了Call方法,看过Retrofit的源码我们知道,Retrofit有个OkHttpCall实现了Retrofit的Call接口,Retrofit的Call几乎是拷贝了OKHttp的Call接口,这里面就是OKhttp的核心请求方法。RealCall是Call的实现类。
我们注意到还有一个AsyncCall,继承自NamedRunnable,NamedRunnable是个简单包装了线程名字的Runnable,不再铺开,可以当做AsyncCall只是实现了Runnable接口
同步execute传的是RealCall,异步enqueue传的是AsyncCall。
同步方法execute,核心方法是
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
异步核心方法:
client.dispatcher().enqueue(new AsyncCall(responseCallback));
我们看到同步比异步多了一句getResponseWithInterceptorChain(),这里先放一下,后面呢会说到,可以记做①。
client是传进来的OKHttpClient,得到Client里面的Dispatcher,先简单看这个Dispatcher
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque runningSyncCalls = new ArrayDeque<>();
我们看它的参数就能猜到,这玩意儿是个Pool,接受两种Call,分别有对应的集合存储:
1、是RealCall,也就是我们现在看的RealCall
2、是AsyncCall,也就是上面异步方法的AsyncCall
回来看execute和enqueue,
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
}
promoteAndExecute();
}
我们看到executed只是把RealCall放到了集合里面,而enqueue放到集合里面后又执行了promoteAndExecute()
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
其实就是把所有readAsyncCall中的Runnable都拿出来扔到executorService中执行了。看下面的代码知道 executorService就是个ThreadPoolExecutor,其实就是单纯的是个线程池了。
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
扔到线程池中执行Runnable(也就是AsyncCall)的executeOn方法.
void executeOn(ExecutorService executorService) {
...
try {
executorService.execute(this);
} ...} finally {
client.dispatcher().finished(this); // This call is no longer running!
}
}
...
@Override protected void execute() {
...
Response response = getResponseWithInterceptorChain();
...
responseCallback.onResponse(RealCall.this, response);
}
}...} finally {
client.dispatcher().finished(this);
}
}
我们看executeOn就是再线程中执行自己,其实就是调用了execute()方法。
execute()中执行了就是getResponseWithInterceptorChain()了,我们记起来我们之前同步的时候还放了一个①。所以绕来绕去同步异步又回到了同一个起点,都到了同一个入口。
大头getResponseWithInterceptorChain(),加个分割线庆祝一下。
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List 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);
}
OKHttp的精髓,责任链模式
简单来说就是链式结构所有的处理器都是一个Chain。前面处理完的处理结果丢给后面,前面处理不了或者不想处理的无脑丢给后面。
我们看上面代码,list添加了如下的interceptor
1. addAll 用户自定义的interceptors
2. add retryAndFollowUpInterceptor
3. add BridgeInterceptor
4. add CacheInterceptor
5. add ConnectInterceptor
6. add networkInterceptors
7. add CallServerInterceptor
我们后面依次讲这些Interceptor的作用,先把流程顺下去。然后新建了一个Chain-RealInterceptorChain,然后调用了chain的proceed方法。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
...
// 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);
...
return response;
}
刨掉if-else和try-catch代码就上面几句,得到list中的Interceptor,得到list在index位置的Interceptor并调用它的intercept,把Chain传进去。这里只是调用了第1个interceptor,所以需要在每个interceptor中调用Chain.proceed(),这样Chain就串起来了。
所以如果我们自定义了一个interceptor叫做MyInterceptor的话,这个MyInterceptor会被在
interceptors.addAll(client.interceptors());
这里被add到list里面,会被第一个就调用,我们必须要在它里面写
//处理request
Response response = chain.proceed(request);
//处理response
以便让Chain串起来,如果我们想处理request就需要在这句之前写,处理response就在这句之后写。所以处理之前之后的数据都可以。就看写在chain.proceed(request);的哪个位置。
这样网络连接就串起来了。
大致流程图如下: