前言
OkHttp可以说是最主流的网络请求框架了,很多项目是直接使用Retrofit 2.0提供的接口进行网络请求,Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。想了解Retrofit请移步Retrofit 2.0 源码分析,retrofit是负责接口封装,okhttp才是真正的网络请求,今天我们就一起探究整个okhttp请求的过程,本章节并不会着重讲怎么使用,主要阅读源码了解内部的部分机制以及一些核心类的作用。
初始化OkHttpClient
OkHttpClient mOkHttpClient = new OkHttpClient();
OkHttpClient实例是通过建造者模式通过Builder类进行创建的
public OkHttpClient() {
this(new Builder());
}
我们看一下builder初始化了哪些参数
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
}
构造一个Request,也是通过建造者模式创建的
Request request = new Request.Builder()
.url(url)
.build();
Request类是HTTP请求,它携带了请求地址、请求方法、请求头部、请求体以及其他信息。它也是通过Builder模式创建的。
private 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;
}
开始请求
Response response = mOkHttpClient.newCall(request).execute();
看一下 OkHttpClient.newCall(request),做了什么,RealCall类实现了Call接口,下面展示RealCall()方法的代码
protected RealCall(OkHttpClient client, Request originalRequest) {
this.client = client;
this.originalRequest = originalRequest;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);
}
接着是RealCall的execute()方法。execute()方法是同步方法,即一直等待http请求, 直到返回了响应. 在这之间会阻塞进程, 所以通过同步方法不能在Android的主线程中执行, 否则会报错。
OKHttp提供了execute(同步方法)和enqueue(异步方法),下面我们先看看execute(同步方法)
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
execute()方法,首先判断是否已执行过,如果已经执行过,则抛出异常信息,也就是说一次Call实例只能调用一次execute()方法,和我们之前说的一样。
如果未执行,则调用Dispatcher类的executed()方法将该Call加入到一个双端队列中
private final Deque runningSyncCalls = new ArrayDeque<>();//双端队列
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
接着调用getResponseWithInterceptorChain()方法返回Response对象,最后finally中,调用Dispatcher的finished()方法,在从已经执行的双端队列中移除本次Call。
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
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();
}
}
通过上面分析,真正发出网络请求,返回结果应该是getResponseWithInterceptorChain()方法,那么接下来,主要看看这个方法
private Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(
retryAndFollowUpInterceptor.isForWebSocket()));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
该方法是组装各种拦截器为一个拦截器链,最后调用RealInterceptorChain的proceed()方法,来处理这个请求
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpStream, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
Connection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpStream != null && !sameConnection(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpStream != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpStream, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpStream != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
return response;
}
拦截器Interceptor和拦截器链Chain都是接口
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
Connection connection();
}
}
下面用一张流程图来说明拦截器链递归从拦截器中返回Response
下面再看看enqueue()方法(异步方法),指在另外的工作线程中执行http请求, 请求时不会阻塞当前的线程, 所以可以在Android主线程中使用。
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
首先都校验这个Call是否已经被执行,如果执行过,就报异常。如果为执行,则调用Dispatcher分发器的enqueue()方法。看下AsyncCall这个类,
final class AsyncCall extends NamedRunnable {
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);
}
}
}
NamedRunnable是实现了Runnable接口,AsyncCall–实际上是一个Runnable,在run()方法中调用了execute()方法,在该方法中调用getResponseWithInterceptorChain()
Response response = getResponseWithInterceptorChain();
拿到请求结果,接下来看一下client.dispatcher().finished(this)做了什么
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();
}
}
finished()方法先从runningAsyncCalls(异步请求队列)删除已经执行的异步请求,然后接着调用了promoteCalls()方法
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) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
首先runningAsyncCalls(异步请求队列) readyAsyncCalls和异步调用准备任务)两部判断,接着循环readyAsyncCalls),将call加入到runningAsyncCalls中,并在readyAsyncCalls删除掉该call,接着线程池执行call
继续看Dispatcher分发器的enqueue()方法
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
如果runningAsyncCalls的大小小于最大请求数量(最大线程数量、并发数量)并且call小于最大主机请求限
制,那么将call 加入到runningAsyncCalls中,接着线程池执行call;否则,将call加入到readyAsyncCalls(异步调用准备任务)。
PS: runningCallsForHost()方法,循环判断cal的host和runningAsyncCalls的中的call的host相同的数量。同一请求是否超过想过请求同时存在的最大值
private int runningCallsForHost(AsyncCall call) {
int result = 0;
for (AsyncCall c : runningAsyncCalls) {
if (c.host().equals(call.host())) result++;
}
return result;
}
接下来看一下Dispatcher类,这个是核心类,Dispatcher是异步请求的策略
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private Runnable idleCallback;
/** Executes calls. Created lazily. */
private ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque runningSyncCalls = new ArrayDeque<>();
...
}
int maxRequests = 64: 最大并发请求数为64
int maxRequestsPerHost = 5: 每个主机最大请求数为5
Runnable idleCallback:Runnable对象,在删除任务时执行
ExecutorService executorService:消费者池(也就是线程池)
Deque
Deque
Deque
最后看一下整个请求的流程图
整个OkHttp源码分析请求过程就结束了
总结
基本上 OkHttp 的请求响应的流程就讲完了,内容有点多。本章节只是分析了主要部分的源码,先对整个结构要有一个系统性的了解,还有很多细节没有展开,比如连接池、缓存策略等等一些机制的实现,有兴趣可以单独深度去了解。