okhttp是目前非常常用的网络请求框架,我们在使用它的同时也要看他是怎么实现的,这篇文章我们简单分析以下它的请求流程。
参考并感谢:https://blog.csdn.net/json_it/article/details/78404010
https://blog.csdn.net/dingding_android/article/details/51942000
https://blog.csdn.net/mwq384807683/article/details/71173442?locationNum=8&fps=1
1、使用(同步请求)
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("http://www.baidu.com")
.build();
Response response = client.newCall(request).execute();
首先我们new了一个OkHttpClient对象,这个类包含了我们网络请求需要的协议,http版本协议,连接池,调度器等。之后利用Builder模式创建了请求对象Request,默认的请求方式是get。最后通过newCall(request)创建了Call对象并执行了该对象的excute方法。具体分析来看源码:
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
@Override
public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
我们newCall方法实际是新建了一个RealCall对象,那么RealCall对象又是什么呢,RealCall实际上是实现了Call接口
public interface Call {
Request request();
Response execute() throws IOException;//同步请求,会在当前线程执行请求并阻塞当前线程
void enqueue(Callback responseCallback);//异步请求,后面会分析
void cancel();
boolean isCanceled();
interface Factory {
// 根据一个Http请求生成一个OKHttp请求。
Call newCall(Request request);
}
}
那么RealCall是怎么实现这些方法呢?
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
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);
}
}
从代码中可以看到,首先判断这个请求是否是第一次执行,如果是,则继续向下执行,否则会抛出一个异常,也就是说一个请求只能执行一次。
之后,调用client.dispatcher().executed(this),即调用调度器的excuted方法,将当前请求加入请求队列中。内部具体的实现我们在后面异步请求中分析。
之后调用getResponseWithInterceptorChain()得到响应Response,并返回。同步请求分析到这里就结束了,重头戏在后面——异步请求。
2、异步请求
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
这里我们使用RealCall的enqueue方法并传入一个CallBack对象来进行回调
我们来看下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));
}
同样的,异步请求首先也要判断该请求是否是第一次执行。
之后调用 client.dispatcher().enqueue(new AsyncCall(responseCallback)),我们的callback对象被当作参数封装进了AsynCall对象,我们来看下AsynCall对象的源码
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
private final boolean forWebSocket;
private AsyncCall(Callback responseCallback, boolean forWebSocket) {
super("OkHttp %s", originalRequest.url().toString());
this.responseCallback = responseCallback;
this.forWebSocket = forWebSocket;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
Object tag() {
return originalRequest.tag();
}
void cancel() {
RealCall.this.cancel();
}
RealCall get() {
return RealCall.this;
}
/**
过一遍拦截器链,并执行请求,然后调用回调函数。
*/
@Override protected void execute() {
// 保证onFailure最多只会被调用一次
boolean signalledCallback = false;
try {
// 进入连接器链,并执行请求
Response response = getResponseWithInterceptorChain(forWebSocket);
// 如果请求被取消,调用onFailure
if (canceled) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
// 正常情况,调用onResponse
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
// 如果上面有调用过回调,就不调了,这里保证onFailure只会被调用一次
if (signalledCallback) {
logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
// 运行队列移除请求
client.dispatcher().finished(this);
}
}
}
可以看到,AsynCall实际上本身是个任务,执行时会调用execute方法,在该方法中我们先调用Response response = getResponseWithInterceptorChain(forWebSocket),通过责任链来获取response,并执行对应的回调函数。那么getResponseWithInterceptorChain()这个方法,就是发送请求和获取响应的主要核心了,我们看看具体是怎么实现的:
Response getResponseWithInterceptorChain() throws IOException {
List 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, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
这里面我们将自定义的拦截器和okhttp自身的拦截器加入至list中,并封装成一个责任链对象Interceptor.Chain,最后调用chain.proceed(request)来执行我们的请求并获得响应。那我们再来看看chain.proceed里干了些什么
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
......
// Call the next interceptor in the chain.
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;
}
可以看到,proceed方法中判断index(此时为0)是否大于或者等于client.interceptors(List )的大小。由于httpStream为null,所以首先创建next拦截器链,主需要把索引置为index+1即可;然后获取第一个拦截器,调用其intercept方法。在intercept方法中我们再一次调用了chain.proceed的方法去调用下一个拦截器的intercept方法。这样我们的request请求层层向下传递,而最后一个拦截器处理完毕时,返回的response又层层向上返回,最后得到的就是最终经过加工后的响应。
那么这些拦截器有那些呢?
1)在配置 OkHttpClient 时设置的 interceptors;
2)负责失败重试以及重定向的 RetryAndFollowUpInterceptor;
3)负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor;
4)负责读取缓存直接返回、更新缓存的 CacheInterceptor;
5)负责和服务器建立连接的 ConnectInterceptor;
6)配置 OkHttpClient 时设置的 networkInterceptors;
7)负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。
请求流程分析到这里,我们知道我们的请求都是通过调度器将请求加入请求队列中,所以我们最后再来看一下dispatch是如何工作的。
public final class Dispatcher {
/** 最大并发请求数为64 */
private int maxRequests = 64;
/** 每个主机最大请求数为5 */
private int maxRequestsPerHost = 5;
/** 线程池 */
private ExecutorService executorService;
/** 准备执行的请求 */
private final Deque readyAsyncCalls = new ArrayDeque<>();
/** 正在执行的异步请求,包含已经取消但未执行完的请求 */
private final Deque runningAsyncCalls = new ArrayDeque<>();
/** 正在执行的同步请求,包含已经取消单未执行完的请求 */
private final Deque runningSyncCalls = new ArrayDeque<>();
再调度器中,创建了一个线程池
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
该线程池没有核心线程,随时创建更多线程,阻塞队列只保留一个任务,当该任务被执行,下一个任务才能进入队列。线程的存活时间为60秒,60秒还没有任务需要执行则线程销毁。
之后我们来看enqueue方法
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
可以看到如果正在执行的请求总数<=64 && 单个Host正在执行的请求<=5,则将请求加入到runningAsyncCalls集合中,紧接着就是利用线程池执行该请求,否则就将该请求放入readyAsyncCalls集合中。上面我们已经说了,AsyncCall是Runnable的子类(间接),因此,在线程池中最终会调用AsyncCall的execute()方法执行异步请求。
这就是okhttp的工作流程,就分析到这里。