一:先看看okhttp简单的配置以及使用:
1. 在app的module中先配置依赖 implementation'com.squareup.okhttp3:okhttp:3.10.0'
2.okhttp执行网络请求调用方式:
异步请求调用Call.enqueue();同步请求调用Call.execute()
二:框架基本流程源码剖析
由于OkhttpClient内部有非常复杂且多的参数配置,作为一个框架来说,为了让用户使用起来比较友好,采用了建造者模式,来构建OkhttpClient所需要的一些重要的参数配置项,这里就不用多说了。
然后我们先分析下一步代码Call call = okHttpClient.newCall(request);这里通过newCall得到了一个Call对象,根据代码调用链可以看出,newCall实际创建的是一个名叫RealCall的对象。
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public CallnewCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
static RealCallnewRealCall(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;
}
然后我们继续跟进,先看下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));
}
这里我们可以看到,他在这里做了个判断,如果同一个Call对象调用了多次enqueue,这里回抛出异常。接下来看关键代码,最终okhttp框架将Callback(也就是我们请求的时候传进来的回调函数)包装为了一个AysncCall(这个AysncCall实际是一个Runnable,最终由Dispatcher类中维护的线程池中的线程执行),交给了一个叫做Dispatcher的类,我们继续跟进...
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost){
runningAsyncCalls.add(call);
executorService().execute(call);
}else {
readyAsyncCalls.add(call);
}
}
这里可以看到Dispatcher类里面维护了三个队列,包含运行中的异步请求队列runningAysncCalls,运行中的同步请求队列runningSyncCalls,以及待运行的异步请求队列readyAsyncCalls。在调用enqueue的时候,框架层先判断正在运行中的异步队列runningAysncCalls的个数是否小于最大请求数maxRequests(默认64),并且判断这个即将添加的请求的host在runningAysncCalls中是否小于maxRequestsPerHost(默认5),都满足则将这个封装由请求信息的Call对象添加到runningAysncCalls,并且交给线程池executorService执行,不满足则添加到准备队列readyAsyncCalls。刚才已经说过了AsyncCall是一个Runnable,也就是最终由线程执行到AsyncCall的execute方法,继续...
这里可以看到execute方法最终是通过getResponseWithInterceptorChain()得到Response,然后通过我们在enqueue时传进来的那个Callback将结果回调出去。追踪同步请求代码调用链也发现,最终也是这个方法返回得到Response,那么接下来看看getResponseWithInterceptorChain
从getResponseWithInterceptorChain这个方法,框架是将一些Interceptor添加到一个list中,然后创建了一个RealInterceptorChain,调用了它的proceed方法
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection)throws IOException {
//************省略代码
// 这里又创建了一个RealInterceptorChain,不过这里有个关键参数不一样,那就是index +1,这里逻辑是先处理拦截器集合的Interceptor的intercept方法,处理每个拦截器自己的逻辑,然后通过index+1,取下一个拦截器执行interceptor.intercept
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;
}
其实这里是一个责任链模式的一个应用,okhttp框架将网络请求的一些步骤封装成了好几层(也就是拦截器interceptor),根据之前上面getResponseWithInterceptorChain的截图可以看到,有以下,简单描述下各自负责的内容:
RetryAndFollowUpInterceptor:网络请求重试以及重定向请求
BridgeInterceptor:主要处理请求里面Header的相关,包括gzip的压缩解压缩,cookie等
CacheInterceptor:这里做了缓存相关策略,比如没网络时但有缓存数据,可以直接返回,还比如说后台返回的数据给了时效性,下次请求的时候看到缓存数据有效,这个时候直接返回缓存数据,节省了网络开销
ConnectInterceptor:这里面有连接池connectionPool,okhttp是基于socket的一个封装,这里有socket连接的缓存
CallServerInterceptor:这里做了最终的网络请求操作与服务端交互
当然,这里可以根据自己的需求自定义拦截器,实现自己的逻辑。然后最终的响应Response通过回调返回后(代码在AsyncCall#execute()),在finally中调用了client.dispatcher().finished(this);这个方法所做的事是从runningAysncCalls中移除这个已经完成的请求,如果条件满足,将readyAsyncCalls中的请求添加到runningAysncCalls队列中并执行
private void finished(Deque calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call))throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback =this.idleCallback;
}
if (runningCallsCount ==0 && idleCallback !=null) {
idleCallback.run();
}
}
private void promoteCalls() {
if (runningAsyncCalls.size() >=maxRequests)return; // Already running max capacity.
if (readyAsyncCalls.isEmpty())return; // No ready calls to promote.
for (Iterator i =readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call)
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >=maxRequests)return; // Reached max capacity.
}
}
至此,关于okhttp一个完整的请求,基本梳理完成。内部的一些拦截器相关内容,后续有时间再深挖。
这里有一些点需要注意:
1.关于okhttp里面Dispatcher的线程池创建,这里采用的是SynchronousQueue
2.Dispatcher的线程池线程数量最大为Integer.MAX_VALUE,疑问:这里不设上限,会不会有性能问题?答:这里其实不会的,虽然这里没做控制但是runningAsyncCalls这个执行中队列有做上限处理,所以不用担心。