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
,必须要深入研究其拦截器的内部实现,后面我将会挨个分析各个拦截器的具体实现。