从用法开始,一般的用法是:
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
void post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Call mCall = client.newCall(request);
mCall.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
}
先创建一个Request,然后再创建一个Call,然后调用call的enqueue发送异步请求。
先看OKHttpClient这个类的描述。
Factory for {@linkplain Call calls}, which can be used to send HTTP requests and read their responses.
<h3>OkHttpClients should be shared</h3>
OkHttp performs best when you create a single {@code OkHttpClient} instance and reuse it for all of your HTTP calls. This is because each client holds its own connection pool and thread pools. Reusing connections and threads reduces latency and saves memory. Conversely, creating a client for each request wastes resources on idle pools.
大意就是说让我们复用OkHttpClient实例,每个请求新建一个client会浪费资源。
看看call是如何创建的
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
RealCall里面:
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;
}
直接创建了一个RealCall的实例。
在看call的enqueue方法:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
主要部分就是,调用了dispatcher的enqueue,继续看dispatcher的enqueue方法。
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
判断了一下是否超过同时发生请求的最大限制,然后将请求放入等待队列或者直接放入executorService,看到这里就应该很熟悉了,这是java的线程池。
然后看他放入的这个AsyncCall的run方法,应该就是发送请求的开始的地方:
@Override protected void execute() {
boolean signalledCallback = false;
try {
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
这个AsyncCall被封装了一下,主要是用于给Thread改一个名字。这里execute方法是在父类的run方法里执行的。
最主要的就是一句:
Response response = getResponseWithInterceptorChain();
看这个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, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
这里组装了一堆interceptor,然后创建了一个chain接口的实例,来看看interceptor和chain接口
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();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
interceptor接口只有一个intercept方法,但是他的参数是chain接口。
chain接口有很多方法, 其中一个比较重要的就是proceed方法。
接着看RealInterceptorChain这个类:
//RealInterceptorChain的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;
}
新建了一个RealInterceptorChain的实例,但是传入的参数index+1了。获取当前的interceptor调用intercept方法。
看一下整体的类图:
上面只画了我们直接接触的类,发送请求主要是组建Call类的实例,call类里引用了很多东西,比如okhttpclient,和request。
他的主要功能都是interceptor完成的。
如果我们自己不添加任何自定义interceptor,那么就只会有5个interceptor,第一个调用的就是RetryAndFollowUpInterceptor
,但是他会调用下一个chain的proceed才返回,所以他也是最后一个返回的。
调用关系图:
每一次interceptor调用proceed方法的时候,都会再创建一个chain的实例,然后把下一个interceptor的index传进去,从而实现了interceptor的遍历调用。
调用顺序如下图:
其中interceptors和networkInterceptors都是可以在创建httpclient的时候添加的,其中interceptors是整个请求的拦截,可以拦截包括okhttp里的一些已有的功能。networkInterceptors主要是部分拦截网络部分。
如果一个网址从A链接重定向到B链接,如果添加的是interceptors,那么就只会调用一次,如果是添加的networkInterceptors,那么会调用两次。
interceptors
- 不需要考虑重中间状态的返回,比如重定向和重试。
- 只会调用一次,即使结果是从缓存中返回的。
- 监控原始请求,不需要考虑okhttp本身加入的一些headers比如
If-None-Match
。 - 允许直接拦截,并且不调用
Chain.proceed()
。 - 允许重试和多次调用
Chain.proceed()
。
Network Interceptors:
- 可以操作一些内部的返回,比如重定向的返回和重试的返回。
- 当被拦截的时候不会被调用。比如命中缓存。
- 监控那些会被发送到网络中的请求。
- 可以访问带有请求数据的
Connection
类。
再来找几个interceptor看看。
retryAndFollowUpInterceptor的intercept方法。
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
里面有一个while(true),也就是通过多次调用Chain.proceed
的方式实现多次请求。他里面调用的proceed是reallInterceptorChain的方法,而不是chain接口的方法,主要是为了复用StreamAllocation
。
BridgeInterceptor是为了将用户的请求转化为正式的网络请求(添加补充一些header等等),然后把网络的返回转换为用户接受到的返回,比如gzip解压缩等等。
CacheInterceptor是用来处理缓存的,根据请求先从缓存里找,如果有就直接返回,否则请求网络,并且将更新缓存。
ConnectInterceptor是用来跟目标服务器建立连接,然后发送请求。
CallServerInterceptor这是最后一个interceptor,用来请求服务端的interceptor。
总结:
- 新的请求来了可能放入等待队列,每次有请求完成就会检查等待队列并且加入发送。所以会有一个listener监听请求是否完成等状态。
- retryAndFollowUpInterceptor是第一个调用的interceptor,如果不添加自定义interceptor的话,他是通过在while循环里多次调用chain.proceed来实现多次发送请求。
- 如果想要拦截一次网络请求,不需要管重定向,重试等等,则使用intercpetors。如果是想要监听所有的发出去的网络请求(包括重定向,重试,也可能命中缓存不会调用这个拦截器),则使用networkInterceptors。
- response.body().string()会将source关闭,所以只能调用一遍。