前言
okhttp作为一个强大的网络请求框架,网络请求是它的核心。okhttp的网络请求有两种:同步请求和异步请求。本文将基于okhttp-3.10.0分析这两种请求的过程。
预备知识
下面是一些预备的知识,在网络请求过程中,会用到一些类,所以先分析一下这些类。
OkHttpClient、Request和Call的创建
在使用okhttp的时候,需要先创建OkHttpClient、Request和Call对象。
创建OkHttpClient
首先是OkHttpClient的创建,有两种方式创建OkHttpClient。
- 默认方式
1
OkHttpClient okHttpClient = new OkHttpClient();
这种方式调用了无参构造器1
2
3
4
5
6
7
8
9
10
11public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
//参数设置为builder提供的默认值
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
...
}
这种方式下,参数都设置为Builder提供的默认值
- Builder模式
1
2
3
4OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.build();
OkHttpClient.Builder的build方法:1
2
3public OkHttpClient build() {
return new OkHttpClient(this);
}
第二种是利用Builder模式创建,通过这种方式创建,可以在创建Builder时链式添加自定义的参数。例如上面自定义了连接超时时间、读取超时时间。
创建Request
创建Request只有一种方式,那就是通过Builder模式,例如:1
2
3Request request = new Request.Builder()
.url("https://www.wanandroid.com/project/tree/json")
.build();
这是因为Request没有提供共有的构造器,需要通过Request.Builder的build方法创建Request实例:1
2
3
4
5
6
7
8
9
10
11
12 public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
创建Call
Call是一个接口,它的唯一实现类是RealCall,通过OkHttpClient的newCall方法创建:1
2
3public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
之后调用RealCall的newRealCall方法:1
2
3
4
5
6
7
8
9
10
11
12
13static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
RealCall call = new RealCall(client, originalRequest, forWebSocket);
//创建EventListener,外界可通过EventListener的回调在某个节点做相关操作
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
在创建RealCall时主要是初始化了一些实例。
Dispatcher
Dispatcher也是一个比较关键的类,它主要用于保存和移除异步请求和同步请求,并控制它们的执行,主要成员变量如下:
成员变量
1 | //最大并发请求数 |
构造方法
Dispatcher有两个构造方法,其中一个构造方法可以指定执行异步请求的线程池:1
2
3public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
另一个构造方法没有指定线程池,使用默认的线程池,且是在第一次进行请求时才初始化线程池:1
2
3
4
5
6
7
8
9
10public 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;
}
默认的线程池属于CachedThreadPool,适合执行大量且耗时较少的任务。
网络请求过程分析
同步请求
RealCall#execute
在进行同步请求时,会调用RealCall的execute方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public Response execute() throws IOException {
synchronized (this) {
//先判断该Call是否被执行,一个Call只能执行一次
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this); //回调,外界可在这时做相关操作
try {
//获得OkHttpClient中的Dispatcher对象,调用Dispatcher的executed方法
//将当前请求加入同步请求队列
client.dispatcher().executed(this);
//进行一系列拦截操作,并获取返回结果
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
//将当前请求从同步请求队列移除
client.dispatcher().finished(this);
}
}
该方法先判断Call是否被执行过,一个Call只能执行一次。之后会调用Dispatcher的executed方法将Call加入同步请求队列,最后进行一系列拦截操作,并获取返回结果。
先看Dispatcher的executed方法:1
2
3synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
该方法将RealCall加入同步请求队列。
RealCall#getResponseWithInterceptorChain
接下来RealCall会调用getResponseWithInterceptorChain方法,该方法很重要,其实现如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
//在配置OkHttpClient时设置的interceptors
interceptors.addAll(client.interceptors());
//RetryAndFollowUpInterceptor:负责失败重试和重定向
interceptors.add(retryAndFollowUpInterceptor);
//BridgeInterceptor:负责把用户请求转换为发送到服务器的请求,
//并把服务器的响应转化为用户需要的响应
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//CacheInterceptor:负责读取缓存、更新缓存
interceptors.add(new CacheInterceptor(client.internalCache()));
//ConnectInterceptor:负责和服务器建立连接
interceptors.add(new ConnectInterceptor(client));
//在配置OkHttpClient时设置的networkInterceptors
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
//CallServerInterceptor:负责向服务器发送数据,从服务器读取响应数据
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(拦截器)是OkHttp中最核心的一个东西,它不仅负责拦截请求进行一些额外的处理,实际上它把网络请求、缓存、透明压缩等功能都统一了起来,每个功能都是一个Interceptor,它们连接成一个拦截链(Interceptor.Chain),一环接一环地完成一次网络请求,这种方式运用了责任链模式。
拦截器的顺序如下:
- 用户在配置OkHttpClient时设置的interceptors
- RetryAndFollowUpInterceptor:负责失败重试和重定向
- BridgeInterceptor:负责把用户请求转换为发送到服务器的请求,并把服务器的响应转化为用户需要的响应
- CacheInterceptor:负责读取缓存、更新缓存
- ConnectInterceptor:负责和服务器建立连接
- 用户在配置OkHttpClient时设置的networkInterceptors
- CallServerInterceptor:负责向服务器发送数据,从服务器读取响应数据
在创建完拦截链后,会调用拦截链的proceed方法,接下来看RealInterceptorChain的proceed方法:
RealInterceptorChain#proceed
1 | public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, |
该方法在等到当前拦截器和下一拦截链后,调用Interceptor的intercept方法。在各个Interceptor中,会完成自己所负责的功能,最后将结果传递给RealCall的execute方法,并通过该方法返回Response给用户。
各个拦截器具体干了什么请看这篇文章okhttp3源码分析:五大拦截器。
异步请求
进行异步请求时,调用的是RealCall的enqueue方法:
RealCall#enqueue
1 |
|
同样先判断该Call是否被执行,一个Call只能执行一次。之后调用Dispatcher的enqueue方法并传入AsyncCall:1
2
3
4
5
6
7
8
9
10
11 synchronized void enqueue(AsyncCall call) {
//如果异步请求队列中的请求数小于64,并且该Call对应的主机运行数小于5
//就把请求添加进异步请求队列并执行,否则添加到等待队列进行等待
if (runningAsyncCalls.size() < maxRequests
&& runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
如果异步请求队列中的请求数小于64,并且该Call对应的主机运行数小于5就把请求添加进异步请求队列并在线程池中执行,否则添加到等待队列进行等待。
AsyncCall是一个Runnable,它的run方法会执行execute方法,AsyncCall的execute方法实现如下:
AsyncCall#execute
1 |
|
可以看到,异步请求也是通过getResponseWithInterceptorChain执行拦截链并获得Response,只不过这次是在子线程中执行该方法。获得Response后,根据结果进行相应回调。最后将此次的Request从异步请求队列中移除,然后从等待队列还有请求的话就取出下一请求,加入异步请求队列并执行。