okhttp中一次网络请求的大致过程:
-
- Call对象对请求的封装
-
- dispatcher对请求的分发
-
- getResponseWithInterceptors()方法
一、OkHttp同步方法总结:
- 创建OkHttpClient和构建了携带请求信息的Request对象
- 将Request封装成Call对象
- 调用Call的execute()发送同步请求
call.execute实际上调用的是RealCall.execute()方法:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();
transmitter.callStart();
try {
client.dispatcher().executed(this);
return getResponseWithInterceptorChain(); //这行语句的执行在后面拦截器链的介绍中会阐述
} finally {
client.dispatcher().finished(this);
}
}
其中client.dispatcher().executed(this);这个步骤就是将本次call请求加入到runningSyncCalls ArrayDeque队列中
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
其中dispatcher中比较重要的几个变量:
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
OkHttp同步需要注意:
- 送请求后,就会进入阻塞状态,直到收到响应。
二、OkHttp异步方法总结
- 创建OkHttpClient和构建了携带请求信息的Request对象
- 将Request封装成Call对象
- 调用Call的enqueue方法进行异步请求
OkHttp异步需要注意:
- OnResponse 和 onFailure工作在工作线程,即子线程
同步和异步的区别
-
- 发起请求的方法调用不同
同步是调用execute(),异步是调用enqueue()
- 发起请求的方法调用不同
- 2.阻塞线程与否
同步会阻塞线程,异步不会阻塞线程,它会开启一个异步线程去完成网络请求的操作 -
- 同步请求就是执行请求的操作是阻塞式,直到Http响应返回
-
- 异步请求就类似非阻塞式的请求,它的执行结果一般都是通过接口回调的方式告知调用者
equeue()方法后续源码流程:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
其中,AsyncCall其实就是一个Runnable,调用Dispatcher的enqueue()方法
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
asyncCall.callsPerHost().incrementAndGet();
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
其中,runningAsyncCalls表示正在运行的任务队列,主要用于判断并发表示的数量,readyAsyncCalls表示缓存等待请求的队列,
maxRequests = 64–>表示当前正在请求的Runnable最大数
maxRequestsPerHost = 5–>表示当前网络向同一个host请求的最大数
Enqueue方法总结:
-
- 判断当前call
-
- 封装成了一个AsyncCall对象
-
- client.dispatcher().enqueue()
Q1、OkHttp如何实现同步和异步请求?
发送的同步/异步请求都会在dispatcher中管理其状态
Q2:到底什么是dispatcher
dispatcher的作用为维护请求的状态,并维护一个线程池,用于执行请求。
Q3. 异步请求为什么需要两个队列
-
Deque<readyAsyncCalls> :正在等待执行,等待缓存的队列
-
Deque<runningAsyncCalls>:正在运行的任务队列
-
Dispatcher 生产者
-
ExecutorService:消费者池
OkHttp的任务调度
Call执行完肯定需要在runningAsyncCalls队列中移除这个线程
readyAsyncCalls队列中的线程在什么时候才会被执行呢?
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
asyncCall.callsPerHost().incrementAndGet();
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
三、OkHttp拦截器
拦截器是OkHttp中提供一种强大机制,它可以实现网络监听、请求以及响应重写、请求失败重试等功能。
系统提供的内部拦截器链:
-
RetryAndFollowUpInterceptor
重试和失败重定向拦截器,这个拦截器主要做些初始化的工作和创建一个StreamAllocation对象(OkHttp3.0代码中没有该对象),用来传递给后面的拦截器
功能:
(1)创建StreamAllocation对象
(2)调用RealInterceptorChain.proceed(…)进行网络请求
(3)根据异常结果或者响应结果判断是否要进行重新请求最大的请求次数 MAX_FOLLOW_UPS = 20
(4)调用下一个拦截器,对response进行处理,返回给上一个拦截器
-
BridgeInterceptor
桥接和适配拦截器
功能:
(1)是负责将用户构建的一个Request请求转化为能够进行网络访问的请求
(2)将这个符合网络请求的Request进行网络请求
(3)将网络请求回来的响应Response转化为用户可用的Response -
CacheInterceptor
缓存拦截器
BridgeInterceptor和CacheInterceptor主要是用于补充用户请求创建过程中缺少一些必须的Http请求头和处理缓存的一些功能。 -
ConnectInterceptor
连接拦截器,负责建立可用的连接,ConnectInterceptor是CallServerInterceptor的基础。
(1) ConnectInterceptor获取Interceptor传过来的StreamAllocation,StreamAllocation.newStream()
(2)将刚才创建的用于网络IO的RealConnection对象,以及对于与服务器交互最为关键的HttpCodec等对象传递给后面的拦截器
流程功能:
(1)创建一个RealConnection对象
(2)选择不同的链接方式
(3)CallServerInterceptor -
CallServerInterceptor
该适配器主要负责是将Http的请求写进网络的IO流中,并从网络IO流中读取服务端返回给客户端的数据。
RealCall.java
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors()); //用户自定义的拦截器
interceptors.add(new RetryAndFollowUpInterceptor(client));
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, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
OkHttp拦截器总结:
-
- 创建一系列拦截器,并将其放入一个拦截器list中,这list拦截器包含用户、网络和系统内部的拦截器
-
- 创建一个拦截器链RealInterceptorChain,并执行拦截器链的proceed方法。
proceed方法的核心就是创建下一个拦截器
- 创建一个拦截器链RealInterceptorChain,并执行拦截器链的proceed方法。
-
- 在发起请求前对request进行处理
-
- 调用下一个拦截器,获取response
-
- 对response进行处理,返回给上一个拦截器