前言
OkHttp 是Android网络访问框架中的扛把子,在初学时只知道如何使用,但并不能做到知其然,知其所以然。然鹅自己去单纯的看源码,又很难领略到他的核心思想。现在就由我来带你走入他深邃的内心世界吧!
OKhttp源码解析
OKhttp异步请求网络的开端
String url = "http://wwww.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: ");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, "onResponse: " + response.body().string());
}
});
以上是一个基本的网络请求用法,所谓由浅入深,先从OkhttpClient构造方法开始剖析他。
public OkHttpClient() {
this(new Builder());
}
可以看到,采用了建造者模式进行了初始化,可以联想到它能够通过这个Builder设置多个属性。那么接下去,我们需要看一下这个builder里面的内容。
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
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;
pingInterval = 0;
}
对应的,我们查看一下他的内部元素
final Dispatcher dispatcher;//调度器
final @Nullable
Proxy proxy;//代理
final List<Protocol> protocols;//协议
final List<ConnectionSpec> connectionSpecs;//传输层版本和连接协议
final List<Interceptor> interceptors;//拦截器
final List<Interceptor> networkInterceptors;//网络拦截器
final EventListener.Factory eventListenerFactory;
final ProxySelector proxySelector;//代理选择器
final CookieJar cookieJar;//cookie
final @Nullable
Cache cache;//cache 缓存
final @Nullable
InternalCache internalCache;//内部缓存
final SocketFactory socketFactory;//socket 工厂
final @Nullable
SSLSocketFactory sslSocketFactory;//安全套层socket工厂 用于https
final @Nullable
CertificateChainCleaner certificateChainCleaner;//验证确认响应书,适用HTTPS 请求连接的主机名
final HostnameVerifier hostnameVerifier;//主机名字确认
final CertificatePinner certificatePinner;//证书链
final Authenticator proxyAuthenticator;//代理身份验证
final Authenticator authenticator;//本地省份验证
final ConnectionPool connectionPool;//链接池 复用连接
final Dns dns; //域名
final boolean followSslRedirects;//安全套接层重定向
final boolean followRedirects;//本地重定向
final boolean retryOnConnectionFailure;//重试连接失败
final int connectTimeout;//连接超时
final int readTimeout;//读取超时
final int writeTimeout;//写入超时
可以看到,我们能够设置的各项属性都在上面了,先放一边,我们继续对网络请求做进一步分析。
关注到这里
OkHttpClient okHttpClient = new OkHttpClient();
...
Call call = okHttpClient.newCall(request);
call.enqueue(...
通过newCall方法,获得了一个Call对象。继续点进去查看
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
最终获得了RealCall类,是Call的实现。于是call.enqueue实际调用的是ReallCall的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));//1
}
在1处我们看到,最后调用了OkhttpClient的dispatcher的enqueue方法。我们回头了解一下Dispatcher是干嘛的。
Dispatcher解析
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** Ready async calls in the order they ll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven t finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven t finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
...
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
首先,顾名思义,dispatcher大概就是用于派发任务的一个工具。里面我们可以看到定义了最大请求数,线程池,以及请求、正在运行的异步双端队列和正在运行的同步双端队列。线程池我们看到采用了SynchronousQueue,这是一个容量为0的阻塞队列,任何的插入操作必须等待另一个线程的移除操作,任何的移除操作都必须等待另一个线程的插入操作,并且核心线程为0,但最大线程数为无穷,意味着任务的加入,就会开辟非核心线程执行任务。回到他的enqueue方法中
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
上面的意思简单明了,先尝试给异步线程添加任务,如果大于最大限制数,就添加给等待队列。这里我们看到了AsyncCall,让我们来了解一下它。
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
...
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();//1
...
} finally {
client.dispatcher().finished(this);
}
}
}
看到这里,看到了Callback,就应该明白了回调在这里产生,返回的结果就在1处产生的reponse结果中(此处跳转到OKhttp网络通信模块),在...处进行了结果的回调处理。其次,NamedRunnable继承了Runnable接口,于是明白了,AsycCall就是一个Runnable。那么他的excute方法又为啥要重写呢,我们查看NamedRunnable源码
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();
}
大概作用就是给当前线程重新定个名字,然后用excute方法,来代替run方法的执行。我们可以理解为excute就是run方法。(我猜可能就是为了方法名字的统一吧……是不是和名字里也带个Call,就特别绕)。好了,删除这段记忆,只需要知道他是个Runnable就行了。回到他的excute方法中
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();//1
...
} finally {
client.dispatcher().finished(this);//2
}
}
我们看到标示2处,调用了Dispatcher的finished方法,我们点进去查看一下
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");//1
if (promoteCalls) promoteCalls();//2
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
注意标示1处,先是将call从队列中移除,再执行了2中的promoteCalls()方法
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {//1
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);//2
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
在上面1处可以看到,先是从readyAsyncCalls(等待异步运行队列)中取出任务,再看到2处,将取出的任务交给运行队列,同时交付线程池,开辟线程执行任务。至此,Dispatcher这个任务分配器的功能就结束了,那么我们究竟是如何进行网络通信的呢。再次回到AsyncCall的excute方法中
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();//1
...
} finally {
client.dispatcher().finished(this);//2
}
}
此处标示1处即进行了网络的通信,并且将结果用Response保存下来了
OKhttp网络通信——interceptors解析
我们查看一下getResponseWithInterceptorChain()的源码吧!
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
//TODO 责任链
List<Interceptor> interceptors = new ArrayList<>();
//TODO 在配置okhttpClient 时设置的intercept 由用户自己设置
interceptors.addAll(client.interceptors());//1
//TODO 负责处理失败后的重试与重定向
interceptors.add(retryAndFollowUpInterceptor);
//TODO 负责把用户构造的请求转换为发送到服务器的请求 、把服务器返回的响应转换为用户友好的响应 处理 配置请求头等信息
//TODO 从应用程序代码到网络代码的桥梁。首先,它根据用户请求构建网络请求。然后它继续呼叫网络。最后,它根据网络响应构建用户响应。
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//TODO 处理 缓存配置 根据条件(存在响应缓存并被设置为不变的或者响应在有效期内)返回缓存响应
//TODO 设置请求头(If-None-Match、If-Modified-Since等) 服务器可能返回304(未修改)
//TODO 可配置用户自己设置的缓存拦截器
interceptors.add(new CacheInterceptor(client.internalCache()));
//TODO 连接服务器 负责和服务器建立连接 这里才是真正的请求网络
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//TODO 配置okhttpClient 时设置的networkInterceptors
//TODO 返回观察单个网络请求和响应的不可变拦截器列表。
interceptors.addAll(client.networkInterceptors());//2
}
//TODO 执行流操作(写出请求体、获得响应数据) 负责向服务器发送请求数据、从服务器读取响应数据
//TODO 进行http请求报文的封装与请求报文的解析
interceptors.add(new CallServerInterceptor(forWebSocket));
//TODO 创建责任链
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
//TODO 执行责任链
return chain.proceed(originalRequest);
}
这里出现了极其重要的类Interceptor——拦截器。先进行解释一下,首先在1处,添加自定义的Interceptor,比如一些json解析,日志打印等功能型拦截器。再添加OKhttp自带的拦截器。注意2处标示,此处是指在访问通过网络时,才添加的拦截器(毕竟有些结果可以通过缓存得到,就不需要重复拦截)。接下去我们看到执行了Chain的proceed方法,而Chain的实现类RealInterceptorChain,因此我们直接去看后者的proceed方法的神奇之处。
public final class RealInterceptorChain implements Interceptor.Chain {
private final List<Interceptor> interceptors;//1
...
...
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// 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);//2
return response;
}
}
首先看到1处,每一个Chain都持有所有的interceptors。因此只要指定拦截器的位置,他就能够找到那个拦截器。此处只是单纯的找到下一个interceptor。重点来了,看2处,此处传给当前interceptor的是下一个编号的Chain。那么执行的过程就可以理解成如下
1 执行0号chain的proceed方法(即getResponseWithInterceptorChain()方法中,创建的首个Chain),找到第0号interceptor,并创建1号chain给他
2 第0号interceptor执行intercept方法,同时执行1号chain的proceed方法,找到第1号interceptor,并创建2号chain给他.
3 第1号interceptor执行intercept方法,同时执行2号chain的proceed方法,找到第2号interceptor,并创建3号chain给他.
以此类推,直到最后,而每一个Chain则类似信息包,包含了index,处理后的request等等信息,向后传递。是责任链模式的一种很好的应用
同时,每个Interceptor重写的intercept方法中,必须调用chain.proceed(request)方法,将链式继续调用。而Interceptor中不同的写法也会产生不同的效果。
OKhttp同步请求网络
先附上源码
@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);//1
Response result = getResponseWithInterceptorChain();//2
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
//接1处源码
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
同步请求与异步请求的区别在于,他虽然也调用了Dispatcher的excute方法,但是仅仅只是把RealCall传入runningSyncCalls队列当中,并未使用线程池去操作他,而后调用getResponseWithInterceptorChain()进行网络访问,与异步请求没有差异。
总结
先附上最终的总结流程图