okhttp3使用很常见,通常我们使用的时候是这样的:
//创建okHttpClient对象
OkHttpClient mOkHttpClient = new OkHttpClient();
//创建一个Request
final Request request = new Request.Builder()
.url(url)
.build();
Call call = mOkHttpClient.newCall(request);
//请求加入调度
call.enqueue(new Callback() {
@Override
public void onResponse(final Response response) throws IOException {
}
@Override
public void onFailure(Request arg0, IOException arg1) {
// TODO Auto-generated method stub
}
});
看下okhttpClient的构造函数,
public OkHttpClient() {
this.dispatcher = new Dispatcher();
***
}
这里有个dispatcher, 分析下Dispatcher的源码:
public Dispatcher() {
}
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
看到了没,这里有两个构造函数,有一个创建线程池的函数,但并不会在开始调用,可以告诉大家,这里只有当你使用enqueue方法的时候,才会用到这个线程池,okhttp所用的线程池有点类似于newCacheExecutorService,好处是会根据程序的运行情况自动来调整线程池中的线程数量,我这里对这个线程池写过demo,假如你一次有30个请求,它会同步的一次将请求放到非核心线程中,因为非核心线程的数量为系统最大值,所以只要系统有空闲,就会创建线程并顺序处理。
接下来看下Request的build函数,这里用了建造者模式来存放Request的请求参数。
接下来看下Call call = mOkHttpClient.newCall(request);这句话,可以看到真正的调用是RealCall这个类,而这里和okhttp3和okhttp2的区别在于Call这个类,okhttp3中是个接口,而okhttp2中是个对象。
最后是RealCall的enqueue方法,这个方法是重点,我们进去分析下:
client.dispatcher().enqueue(new AsyncCall(responseCallback));
可以看到调用的是Dispatcher的enqueue方法,进去看看,
synchronized void enqueue(AsyncCall call) {
if(this.runningCalls.size() < this.maxRequests && this.runningCallsForHost(call) < this.maxRequestsPerHost) {
this.runningCalls.add(call);
this.getExecutorService().execute(call);
} else {
this.readyCalls.add(call);
}
}
这里判断同时加入的任务有没有超过一些限制,然后执行线程池中的任务,进到Call.AsyncCall方法中:
public abstract class NamedRunnable implements Runnable {
public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(this.name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
}
先看下NamedRunnable 类,可以看到执行了execute方法:
final class AsyncCall extends NamedRunnable {
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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
这里根据返回值的判断,回调给Callback的onResponse方法,注意,因为线程池开启的任务是异步任务,而这里的回调都是在子线程中执行的,因此不能直接去更新UI,这也是单独用okhttp不是很方便的原因。
我们再看RealCall中的方法,有个excute方法,这个方法和enqueue方法都是执行任务,而excute执行的是同步任务,如果你像上面一样写下如下代码:
Call call = mOkHttpClient.newCall(request);
call.execute();
肯定会报错误的,因为这时执行的时候会认为在主线程执行的http请求,而http请求是必须要在子线程执行的。
我们再看下Response response = getResponseWithInterceptorChain();这个函数,大家应该都很好奇okhttp看到这里,好像也没什么特别的,封装了两种方式,异步和同步的请求,开了个线程池,那么真正的请求在哪里做的呢?答案就在这个方法里面,我们进去看下:
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);
}
这里给interceptors添加了6个拦截器,然后调用RealInterceptorChain的proceed方法,再进去看下:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection) throws IOException {
***
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
***
return response;
}
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
}
代码很长,核心内容在这里,仔细分析一下,从interceptors里面根据index索引取出拦截器,然后调用intercept方法,并传入RealInterceptorChain 对象,而在intercept方法中都有调用RealInterceptorChain 的proceed方法,这说明什么?他们在循环调用啊!!!我勒个去。。。。根据index的不同,intercept方法中传入的是下一个拦截器对象。那么为什么要这样呢?拦截器里面做了什么,看来我么要去分析下拦截器里面的intercept方法了,先看第一个retryAndFollowUpInterceptor:
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
这里是循环调用的体现,流程是先去处理其他5个拦截器,得到response之后再来这边处理,那么接下来看第二个拦截器BridgeInterceptor,同样在intercept方法里面调用了如下函数:
Response networkResponse = chain.proceed(requestBuilder.build());
意味着再去处理剩余的4个处理器,等待response进行处理,如此类推,最后处理的拦截器就是CallServerInterceptor,我们看下intercept方法:
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
httpCodec.writeRequestHeaders(request);//写入请求头
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {//判断是否有body数据
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return what
// we did get (such as a 4xx response) without ever transmitting the request body.
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
} else if (!connection.isMultiplexed()) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection from
// being reused. Otherwise we're still obligated to transmit the request body to leave the
// connection in a consistent state.
streamAllocation.noNewStreams();
}
}
httpCodec.finishRequest();
if (responseBuilder == null) {
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
}
看到了,这里就是真正的http请求,但我们发现这里的http请求不是用HttpUrlConnection,那他是怎么做的呢?
先是封装了Request对象,然后写入到HttpCodec中,接着判断是否带body数据,如果携带了,写入到request.body()中,然后刷新httpCodec,httpCodec中的source和sink,对应着socket请求中的输入和输出,从HttpCodec中读取响应的头信息,返回Response.Builder。
httpCodec.finishRequest();//这里是真正的执行请求
if (responseBuilder == null) {
responseBuilder = httpCodec.readResponseHeaders(false);
}
最后拿到Response数据:
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
后面是拿到的response数据进行处理。
前面说了,其他的拦截器都是对这里的Response进行结果的处理。CacheInterceptor主要是对结果进行缓存的控制,retryAndFollowUpInterceptor对重定向情况的处理。