OkHttp是Square公司开发的用于网络请求的第三方框架,这几年非常流行,也是很多面试会问到的东西,今天我们来分析一下部分实现。
一般使用方式是新建一个OkHttpClient对象,通过Builder来构建一个Request对象,最后通过OkHttpClient的newCall方法来实现构建Call的实现类,最后通过enqueue方法来进行请求:
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url("https://www.baidu.com")
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "fail");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, "response : " + response.toString());
}
});
在Callback里面进行回调,如果请求成功并且没有异常,则在onResponse里面输出response的主体,反之调用onFailure方法。
OkHttpClient只是new了一下,这里先不分析,Request类我们可以看到构造方法是不可见的,只能通过内部的Builder方式来构建实例,通过建造者模式传入url等参数。Call实例的构建是通过OkHttpClient来进行:
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
可以看到是直接返回了一个RealCall,Call是一个借口,RealCall是他的实现,从字面意义我们也可以看出来这个类是“真正的”Call。接着enqueue方法也是由Realcall来执行:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
先进行是否执行过的判断,如果已经执行过enqueue或者execute方法,executed被标记为true,再次调用这两个方法就会抛出异常。captureCallStackTrace是添加相应的信息记录。然后是通过OkHttpClient的Dispatcher.enqueue,Dispatcher是通过OkHttpClient的Builder来创建的,字面意思是一个调度器。Dispatcher的文档是:
/**
* Policy on when async requests are executed.
* //异步请求执行策略,每个Dispatcher对应一个线程池
* Each dispatcher uses an {@link ExecutorService} to run calls internally. If you supply your
* own executor, it should be able to run {@linkplain #getMaxRequests the configured maximum} number
* of calls concurrently.
*/
是通过线程池来执行具体的Call,从这里我们也可以看出传进来的Call应该继承了Runnable接口:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//条件判断,正在请求的个数小于最大限制,默认最大限制64,也就是最大线程数量限制
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
//大于限制,加到另外一个队列中先做存储
readyAsyncCalls.add(call);
}
}
通过线程池及一些策略来执行Call接口,这里的Call指的是AsyncCall:
final class AsyncCall extends NamedRunnable {//最终继承自Runnable
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
@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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
Callback通过构造函数传入,它的execute方法也是线程池执行到会调用的方法,最重要的也就是Response的获取,也就是这一句:
Response response = getResponseWithInterceptorChain();
后面都是对于Response的解析和callback的回调,以及一些善后工作。
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);
}
创建了一堆拦截器来执行各种策略,先是client我们自己添加的拦截器(默认没有),然后是retryAndFollowUpInterceptor,这个是RealCall的构造函数中新建的,等等,最后创建了一个RealInterceptorChain对象并将之前添加的拦截器和request传进去,然后调用proceed方法:
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
其实调用的是另一个重载的方法:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
Connection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;//保证proceed方法只被调用一次
//省去部分代码
// 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);
//省去部分代码
return response;
}
这里的streamAllocation,httpCodec和Connection在构造函数里面有一次初始化,但是我们发现上面构造的时候传入的都是null,只有一个index作为初始下标,但是RealInterceptorChain还分别有三个方法来修改这三个成员:connection(),streamAllocation(),httpStream(),但是找了一圈也没有找到这些方法的调用地方,然后发现是在不同的Interceptor中会调用proceed方法,而在这个方法里面,RealInterceptorChain会被重新初始化,也就是每个interceptor对应一个RealInterceptorChain。
这里使用了责任链模式,在proceed的时候会重新构建RealInterceptorChain,然后每次interceptor获取之前保存的下一个,最后调用intercept方法,而在每个interceptor的intercept方法中,又会重新调用proceed方法,但是每次传入的参数不同。在RetryAndFollowUpInterceptor中会传入StreamAllocation,而在ConnectInterceptor中会传入httpCodec和connection,其他interceptor只是调用chain.proceed(request),也就是只传入一个request,这里难以理解的一点是,每个interceptor都是在intercept方法中给下一个interceptor准备RealInterceptorChain,ConnectInterceptor就是给CallServerInterceptor准备这些参数,而CallServerInterceptor也是关键的向服务器进行请求的操作:
/** This is the last interceptor in the chain. It makes a network call to the server. */
public final class CallServerInterceptor implements Interceptor {
大体流程到这里分析完毕了 但是每个拦截器还有很多细节,比如缓存方式等,具体的向服务器请求的细节这些。希望后面可以有时间再深入了解