OkHttp源码解读
整体流程
图来自:https://blog.piasy.com/2016/07/11/Understand-OkHttp/index.html
将http的源码过了一遍,感觉它最重要的就是拦截器功能,分为网络拦截器和应用拦截器,网络拦截器可能进行多次,但应用拦截器只进行一次。而拦截器就是在请求发送和接受读取的前后做一系列操作,接下来就让我们深入源码看看他到底做了什么吧
okhttpclient
一般我们在使用时直接加一句OkHttpClient client = new OkHttpClient.Builder()
就完成了OkHttpClient 的构造,那它里面到底做了啥,请看:
//默认的构造方式
public OkHttpClient() {
this(new Builder());
}
// 这里就是利用builder进行初始化,builder是OkHttpClient的一个内部类
private OkHttpClient(Builder 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;
}
ps:建造者模式https://www.jianshu.com/p/afe090b2e19c
request
// 一个request请求实例
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
// 构造函数
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;
}
万万没有想到,这个request也用到了builder进行构造,话说builder用起来倒是很方便咧
发起HTTP请求
然后看一下我们的请求
// 异步请求
client.newCall(request).enqueue(callback);
那么client.newCall(request)
又做了什么呢?
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
这里看到了是调用了RealCall.newRealCall,而方法的override表面这是一个复写的方法,可以在okhttp实现的call接口中看到:
// 工厂模式
interface Factory {
Call newCall(Request request);
}
那这个方法就是一个创建call的接口,而通过call源码中的注释我们也了解到call封装了我们的request,并无法请求两次
/**
* A call is a request that has been prepared for execution. A call can be canceled. As this object
* represents a single request/response pair (stream), it cannot be executed twice.
*/
然后我们再来看看这个RealCall.newRealCall
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;
}
就是构建了一个call并为这个call添加了eventListener,由名字可知是一个事件监听
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
看看realcall里有一个retryAndFollowUpInterceptor,这是一个拦截器,我们之前说了okhttp的核心就是拦截器。
构建完了之后,将请求加入调度call.enqueue(callback)
这里的callback就是一个请求成功和失败的回调,需要我们自己实现。那我们就得去realcall里面看具体的实现方法了,因为我们这里的call是它构建出来的
@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));
}
这里面首先判断了消息是否请求过,又回调了之前的listener的方法。然后新建了asynccall交给了client.dispatcher()
来处理,看到这dispatcher是不是感觉很亲切,因为自己的项目可能也会用到,那么我们又去看看这个dispatcher将请求分发到了哪
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
好吧,又得看看这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 Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
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;
}
...
}
根据注释那我们就知道这几个队列的作用了
synchronized void enqueue(AsyncCall call) {
// 请求的异步线程小于64且请求同一个主机小于5
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//加入异步队列并执行
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
// 不满足就等待
readyAsyncCalls.add(call);
}
}
然后我们发现异步的call都变成了asynccall,我们要去看看这里面的乾坤
final class AsyncCall extends NamedRunnable
发现它继承自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();
}
就是把名字改一下,完了又换回去(这里注意this和Thread.currentThread()的区别https://blog.youkuaiyun.com/dfshsdr/article/details/92760135),有一个复用修改名字的感觉。
然后我们又得去看看execute里做了什么
@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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
我们注意,这里的getResponseWithInterceptorChain()起到了至关重要的作用,他进行了拦截器的调用,最后得到一个response,我们就直接去看看这个方法
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);
}
我们看会发现他是根据我们之前看得那个图里面的流程将拦截器加进去(包括我们自定义的拦截器哦),最后生成了chain,并调用了proceed方法,那我们就知道这个方法才是真正实现了拦截器的作用。
@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++;
// 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, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
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");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
一眼看过去及时有注释我也有点炸…我们重点关注一下
// 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);
你可能会问,为啥这就调用了一个拦截方法,其实不然,你进一个拦截器看看
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
他最后都是回到了proceed,然后回到流程
最后
基本流程就是这样子,讲道理,有点累,初稿完成啦,我后面会再完善完善