OkHttp3.9.0之请求基本流程
我们在使用OkHttp进行网络请求时需要创建一个OkHttpClient实例,然后调用其newCall方法获得一个Call对象,然后调用Call对象的execute或enqueue方法就可以同步或异步执行网络请求了。就像下面这样
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("url")
.build();
Call call = client.newCall(request);
//同步请求
Response response = call.execute();
//异步请求
call.enqueue(new Callback(){
void onFailure(Call call, IOException e){
//...
}
void onResponse(Call call, Response response){
//...
}
})
可以看到,使用OkHttp发起网络请求,大致需要4个步骤
- 创建一个
OkHttpClient对象 - 创建一个网络请求的
Request - 调用
OkHttpClient的newCall方法获得一个Call对象 - 调用
Call对象的execute或enqueue方法来发起网络请求
创建OkHttpClient
OkHttpClient可以使用new关键字创建,也可以使用OkHttpClient.Builder以构造器模式进行创建,具体如何创建就不再举例了,我们主要来看一下OkHttpClient中的一些字段以及其作用
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
final Dispatcher dispatcher;//有关执行异步请求的策略。
final @Nullable Proxy proxy;//代理
final List<Protocol> protocols;//支持的协议,http、http1、http2、spdy等
final List<ConnectionSpec> connectionSpecs;//连接规格,当建立SSL/TLS连接时会用到,里面包含支持的TLS版本以及加密套件等
final List<Interceptor> interceptors;//拦截器
final List<Interceptor> networkInterceptors;//网络拦截器
final EventListener.Factory eventListenerFactory;
final ProxySelector proxySelector;//代理选择器,可以根据URI返回代理
final CookieJar cookieJar;//提供cookie的持久化策略
final @Nullable Cache cache;//response文件缓存
final SocketFactory socketFactory;//socket工厂
final @Nullable SSLSocketFactory sslSocketFactory;//sslSocket工厂
final @Nullable CertificateChainCleaner certificateChainCleaner;//证书清理工具,与CertificatePinner配合使用
final HostnameVerifier hostnameVerifier;//域名验证,用来校验请求域名与服务端返回的证书中的域名是否匹配
final CertificatePinner certificatePinner;//证书锁定,可以用来设置信任指定的证书
final Authenticator proxyAuthenticator;
final Authenticator authenticator;
final ConnectionPool connectionPool;//连接池,用来管理底层的Socket链接,负责链接复用、清理等工作
final Dns dns;
final boolean followSslRedirects;
final boolean followRedirects;
final boolean retryOnConnectionFailure;
final int connectTimeout;
final int readTimeout;
final int writeTimeout;
final int pingInterval;
}
OkHttpClient中的这些字段基本上都是可以定制的,由此可见OkHttp功能的强大。
创建Request
Request只能通过Request.Builder来创建,来看一下它的一些字段
public final class Request {
final HttpUrl url;//请求URL
final String method;//请求方法
final Headers headers;//报文头部
final @Nullable RequestBody body;//请求体
final Object tag;
private volatile CacheControl cacheControl; //负责解析Cache-Control头,并保存相关信息
}
Request包含了一个网络请求的所有基本要素。
RequestBody
RequestBody是一个抽象类,它有三个方法
public abstract class RequestBody {
//对应Content-Type头部字段
public abstract @Nullable MediaType contentType();
//对应Content-Length头部字段
public long contentLength() throws IOException {
return -1;
}
//入参是一个sink对象,用来写入数据
public abstract void writeTo(BufferedSink sink) throws IOException;
}
OkHttp提供了多个静态方法用来方便地创建RequestBody
public static RequestBody create(MediaType, byte[]);
public static RequestBody create(MediaType, byte[], int, int);
public static RequestBody create(MediaType, ByteString);
public static RequestBody create(MediaType, File);
public static RequestBody create(MediaType, String);
想要实现文件上传,只需要调用create(MediaType, File)方法创建RequestBody,然后再用这个RequestBody创建Request,非常方便。
newCall创建Call对象
//OkHttpClient.java
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
//RealCall.java
static RealCall newRealCall(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;
}
OkHttpClient.newCall调用了RealCall.newRealCall创建了一个RealCall对象。
发起网络请求
OkHttpClient支持发起同步或者异步请求,分别对应execute和enqueue方法。
execute
通过newCall创建出来的是一个RealCall实例,我们来看看它的execute方法
public Response execute() throws IOException {
//...
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
//...
throw e;
} finally {
client.dispatcher().finished(this);
}
}
该函数比较简单,主要涉及三个方法调用,我们先来看看Dispatcher的executed与finished方法
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
//finished方法最终会调用到Dispatcher内部的私有finished方法
//calls为runningSyncCalls
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
//...
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
//...
}
//...
}
可以总结出来,Dispatcher在网络请求开始前保存Call对象,在请求结束后移除Call对象,起到管理请求的作用。
我们再来看看getResponseWithInterceptorChain方法,它是OkHttp网络请求的核心方法
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> 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);
}
看到关键字interceptor是不是想到了责任链模式?是的,这里确实使用了责任链模式,因此会依次执行以下拦截器
- 首先会执行我们在构造
OkHttpClient时通过Builder.addInterceptor方法添加的拦截器 RetryAndFollowUpInterceptor用于处理重试以及重定向BridgeInterceptor负责完善请求头部信息,响应消息体的解码工作CacheInterceptor负责缓存策略- 执行我们在构造
OkHttpClient时通过Builder.addNetworkInterceptor方法添加的网络拦截器 ConnectInterceptor负责复用或者创建TCP连接,供之后的网络请求使用CallServerInterceptor负责向服务端发起真正的网络请求
在CallServerInterceptor执行完毕之后,就会获得请求的Response对象。这就是OkHttp进行网络请求时的大致流程。使用了责任链模式,以非常优雅的方式完成了复杂而强大的功能。
enqueue
enqueue的流程与execute基本一致,最大的区别就是它将网络请求封装成一个AsyncCall对象,然后抛到一个线程池中执行,从而达到发起异步请求的功能。
public void enqueue(Callback responseCallback) {
//...
//AsyncCall实现了Runnable接口
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
//Dispatcher.java
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
//...
}
}
可以看到Dispatcher.enqueue方法将AsyncCall对象放入到线程池中等待处理。
当AsyncCall被执行时会触发其execute方法
@Override
protected void execute() {
//...
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
//...
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
//...
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
//...
responseCallback.onFailure(RealCall.this, e);
} finally {
client.dispatcher().finished(this);
}
}
同样是getResponseWithInterceptorChain,上面讲过了我们就不再赘述了。然后就是通过回调的形式返回网络请求的结果。
总结
OkHttp以拦截器模式来实现网络请求的全过程,使得整个网络请求的流程简单、清晰。
想要深入理解OkHttp,必须要深入研究其拦截器的内部实现,后面我将会挨个分析各个拦截器的具体实现。