OKHttp3–详细使用及源码分析系列之初步介绍【一】
OKHttp3–流程分析 核心类介绍 同步异步请求源码分析【二】
OKHttp3–Dispatcher分发器源码解析【三】
OKHttp3–调用对象RealCall源码解析【四】
OKHttp3–拦截器链RealInterceptorChain源码解析【五】
OKHttp3–重试及重定向拦截器RetryAndFollowUpInterceptor源码解析【六】
OKHttp3–桥接拦截器BridgeInterceptor源码解析及相关http请求头字段解析【七】
OKHttp3–缓存拦截器CacheInterceptor源码解析【八】
OKHttp3-- HTTP缓存机制解析 缓存处理类Cache和缓存策略类CacheStrategy源码分析 【九】
写博客快一年了,虽然中间停了一段时间,但是还算是写了不少,有Android系统源码分析的,有Android功能开发相关的,有开发工具相关的,但是从头看下来总是感觉少了点什么,少了啥呢?
没错,就是少了开源框架的这部分!所以接下来的多数博客基本是围绕着一些应用比较广泛的框架进行展开,描述它们的使用方法,源码解读等,比如OKHttp,Retrofit,Glide,Picasso,ButterKnife,EventBus,RXJava等
OKHTTP对于Android开发者的重要性可以说是不言而喻了,这里我采用的阅读源码方法是根据使用方法顺藤摸瓜查看源码和阅读几个重点类的相结合的方法去理解OKHTTP,我认为结合使用情况去阅读更能有代入感,更能加深理解,同时选择几个关键类辅助更能帮我们理解整体框架
所以这里我将以OKHTTP的同步请求方法和异步请求方法作为切入点,同时配有几个关键类去阅读它的源码
在阅读框架源码时一定要对整体流程有个把握,如图:
使用步骤基本分为如下几步:
OkHttpClient httpClient = new OkHttpClient
.Builder()
.connectTimeout(3000, TimeUnit.SECONDS)
.readTimeout(3000,TimeUnit.SECONDS)
.build();
public String syncRequest(String url){
String result = null;
Request request = new Request
.Builder()
.url(url)
.build();
Call call = httpClient.newCall(request);
Response response = null;
try {
response = call.execute();
if(response.isSuccessful()){
result = response.body().string();
}
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
OkHttpClient httpClient = new OkHttpClient
.Builder()
.connectTimeout(3000, TimeUnit.SECONDS)
.readTimeout(3000,TimeUnit.SECONDS)
.build();
public void asyncRequest(String url){
Request request = new Request
.Builder()
.url(url)
.build();
Call call = httpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//请求失败了,这里是在子线程回调,不能在这里直接更新UI
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//获取请求结果,这里是在子线程回调
String result = response.body().string();
}
});
}
从上面的代码可以看出来,不管是同步和异步,在真正执行请求前,也就是execute和enqueue方法调用前所做的操作都是一样的,接下来我们就从这相同部分开始看
我们这里是通过它的静态内部类Builder构建OKHttpClient,也就是使用常见的构建者模式,代码如下
public Builder() {
//实例化分发器
dispatcher = new Dispatcher();
//设置支持的网络协议,默认支持HTTP/2和http/1.1
protocols = DEFAULT_PROTOCOLS;
//设置支持的连接,默认是使用SNI和ALPN等扩展的现代TLS连接和未加密、未认证的http连接
connectionSpecs = DEFAULT_CONNECTION_SPECS;
//Call状态监听器
eventListenerFactory = EventListener.factory(EventListener.NONE);
//使用默认的代理选择器
proxySelector = ProxySelector.getDefault();
//默认没有cookie
cookieJar = CookieJar.NO_COOKIES;
//创建socket工厂类
socketFactory = SocketFactory.getDefault();
//下面四个是安全相关的配置
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
//实例化连接池 使用连接池技术减少请求的延迟(如果SPDY是可用的话)
connectionPool = new ConnectionPool();
//域名解析系统
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
//为了保持长连接,我们必须间隔一段时间发送一个ping指令进行保活
pingInterval = 0;
}
这里有两个很重要的操作
虽然Request构建如下方所示
Request request = new Request
.Builder()
.url(url)
.build();
但是整个代码实现流程是
先通过Request构建内部builder对象,在构建它的过程,默认指定请求方法为get,然后又构建了一个请求头Headers的Builder对象
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
Headers类的Builder对象没有构造方法,只不过内部定义了一个List数组,用来存放头部信息
public static final class Builder {
final List namesAndValues = new ArrayList<>(20);
}
然后通过Request的Builder对象设置url,再调用build方法,该方法实例化了Request对象并返回
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
在实例化Request的时候,Request的构造方法里又调用了内部Builder对象内部的Headers.Builder对象的build方法,这个方法实例化了Headers对象,Headers对象的构造方法将List数组转换成了String数组
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;
}
public Headers build() {
return new Headers(this);
}
Headers(Builder builder) {
this.namesAndValues = builder.namesAndValues.toArray(new String[builder.namesAndValues.size()]);
}
Call在OKHttp中是一个接口,它抽象了用户对网络请求的一些操作,比如执行请求(enqueue方法和execute方法),取消请求(cancel方法)等,真正实现是RealCall这个类
它的实例化是由OkHttpClient对象的newCall方法去完成的
/**
* 准备在将来某个时间点执行Request
*/
@Override
public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
这里RealCall的构造方法第一个参数是OkHttpClient对象,第二个参数是Request对象,第三个参数是跟WebSocket请求有关,这个后续在讲到这点的时候再说;至于在RealCall里真正做了哪些事需要到构造方法看看
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
// TODO(jwilson): this is unsafe publication and not threadsafe.
this.eventListener = eventListenerFactory.create(this);
}
总结:不管是同步请求还是异步请求,在执行请求前都要做这三个操作,也就是实例化OKHttp客户端对象OkHttpClient,包含请求信息的Request对象,一个准备执行请求的对象Call;接下来就要分道扬镳了
Call.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);
}
}
这里我们来看下第三步源码:
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
可以看到这里的逻辑很简单,只是将请求添加到了runningSyncCalls中,它是一个队列,看看它的定义
private final Deque runningSyncCalls = new ArrayDeque<>();
如果大家看过AsyncTask的源码,一定对ArrayDeque这个数据结构熟悉,不熟悉的可以看看博主的从源码解析-Android数据结构之双端队列ArrayDeque;这个队列会保存正在进行的同步请求(包含了已取消但未完成的请求)
接下来我们就要想了,executed方法只是将请求存到了队列当中,那什么时候去执行这个请求呢?上面说到过拦截器和拦截器链的概念,那么我们就回到上面的第四步去看看它的真面目
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 (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
我们通过Call.execute()方法进行同步请求获取最终的返回结果就是通过这个方法实现的,看看内部逻辑:
具体逻辑来看它的内部实现
private final List interceptors;
private final StreamAllocation streamAllocation;
private final HttpCodec httpCodec;
private final RealConnection connection;
private final int index;
private final Request request;
private int calls;
public RealInterceptorChain(List interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
}
@Override
public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
这里的设计有点奇怪,构造方法中已经传了Request ,然后在proceed方法又传一遍
没啥逻辑,就是调用另一个重载的方法
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection 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.httpCodec != null && !this.connection.supportsUrl(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.httpCodec != 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, httpCodec, 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 (httpCodec != 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;
}
我们重点看中间的几行代码:
又实例化了一个RealInterceptorChain对象,然后根据index从interceptors列表中取出下一个拦截器,最后调用该拦截器的intercept方法,并将拦截器链对象传递进去,最终获取结果返回
这里我们要看到一个很重要的点就是在实例化新的RealInterceptorChain时候,传入的参数是index+1;这样当第一个拦截器执行自己的intercept方法,做相关的逻辑,如果能获取结果就返回,不再继续执行接下来的拦截器;如果需要通过下一个拦截器获取结果,那就通过传入的参数RealInterceptorChain调用proceed方法,这样在这个方法里根据递增的索引index就能不断的从interceptors列表中取出下一个拦截器,执行每个拦截器自己的逻辑获取结果,直到所有拦截器执行完毕;这就是OKHttp拦截器链的由来,也是它的精妙所在
至于每个拦截器具体逻辑,在后续文章给出
接下来看看第五步
当队列中的请求执行完毕后该怎么处理呢?难道继续存放在队列中?我们来看看
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();
}
}
第一个参数是正在进行同步请求的队列,第二个参数是Call对象,第三个参数是调整异步请求队列的标志
这里也有一个同步代码块,因为队列是线程不安全的实现;首先将这个Call对象从队列中移出,不能移出就抛出异常;这里promoteCalls是false,所以promoteCalls方法不执行;接下来调用runningCallsCount方法,计算目前正在执行的请求,看看具体实现
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
返回的值是正在进行的同步请求数和正在进行的异步请求数
然后给idleCallback赋值
最后判断runningCallsCount 是否为0,这说明整个dispatcher分发器内部没有维护正在进行的请求了,同时idleCallback不为null,那就执行它的run方法(每当dispatcher内部没有正在执行的请求时进行的回调)
可以看出来,在进行同步请求的时候dispatcher分发器做的事很简单,就是添加请求到队列,从队列移除请求,那异步请求的时候是如何呢?看下方
Call.enqueue
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
这里跟同步请求不同,它构建了一个新的Call,即AsyncCall对象,是RealCall内部类,继承NamedRunnable
NamedRunnable实现了Runnable接口
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
内部逻辑比较简单,设置了线程名,然后调用execute方法,也就是说AsyncCall具体逻辑将在execute方法执行
我们来看看分发器是如何处理异步请求的
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
看看线程池的构造
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;
}
通过构造方法可以看出该线程池没有核心线程,且最大线程数是Integer.MAX_VALUE,基本上是无限大了,保活时间是60s,也就是说工作线程如果空闲时间超过60s,将被回收,关于线程池的理解可以参考博主的Android开发-通过ExecutorService构建一个APP使用的全局线程池
有的童靴可能想了,没有设置最大线程数,那是不是对手机性能消耗是不是特别大;其实不会,因为OKHttp对正在进行的异步请求数有限制,最大是64个
既然要被执行,那就看看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);
}
}
}
在execute方法中也有调用getResponseWithInterceptorChain这个方法,跟同步请求差不多,同时会将请求结果通过Callback返回回去,一定要注意,回调是在子线程执行的,不要直接在这里进行更新UI的操作
最后要注意client.dispatcher().finished(this)这一句,同步请求时也调用了它,我们去看看有什么不同
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
虽然参数不同,但是调用finished方法的三个参数跟同步请求是不同的
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();
}
}
其余代码在同步请求时已经讲过,这里只将一句
promoteCalls为true,那么会调用promoteCalls方法,这是dispatche中很重要的一个方法
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.
}
}
可以看出来这个方法主要是用来调整异步请求队列的
在进行异步请求的时候,dispatcher分发类通过内部的两个队列和一个线程池对请求进行管理,保存传递进来的请求,执行传递进来的请求,移除执行完的请求