今天来拆解一下okhttp;分析一下okhttp如何做到这么好;以及我们需要掌握什么样的技能或者思路去实现一个自己的“okhttp”;那先从入口开始看;
1.OkHttpClient ;先看头部信息下面备注了;
//Cloneable 浅copy ,Call.Factory实现工厂接口(针对于普通接口)WebSocket.Factory实现工厂接口(针对websocket请求的接口)
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
}
a、我们现在先搞明白上面实现接口的目的;第一个Cloneable这个是浅copy使用的;暂时真的没感觉OkHttpClient能在什么场合使用到;但是这个还是提供了;Call.Factory =>主要是实现RealCall这个类;这个类里面是实现普通的网络请求的方式;WebSocket.Factory =>主要实现RealWebSocket这个类;这个类里面实现webscoket的请求方式;
b、看OkHttpClient的参数是什么;下面都进行备注了;
static final List DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.HTTP_1_1);//创建一个只读的;不可以修改的list;设置默认的 http/2 ,http/1.1
static final List DEFAULT_CONNECTION_SPECS = Util.immutableList(
ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT);//创建一个只读的;不可以修改的list;TLS配置
static {
Internal.instance = new Internal() {//创建一些方法里面实现一些需要做的方法;后续讲
...
};
}
final Dispatcher dispatcher;//开非核心线程进行,异步耗时操作(网络请求)
final Proxy proxy;//默认空值
final List protocols;//设置网络类型
final List connectionSpecs;//设置TLS的
final List interceptors;//拦截器,仅支持初始化设置
final List networkInterceptors;//网络拦截器,仅支持初始化设置
final ProxySelector proxySelector;//设置安全的认证操作
final CookieJar cookieJar;//默认没有cookies
final Cache cache;//缓存
final InternalCache internalCache;//网络缓存
final SocketFactory socketFactory;//创建一个对象并将其连接到指定远程端口上的指定远程地址
// 对象还将绑定到提供的本地地址和端口。使用为此工厂建立的对象选项配置对象。
//如果有安全管理器,checkConnect 则使用主机地址port 及其参数调用其方法。这可能会导致SecurityException。
final SSLSocketFactory sslSocketFactory;//设置保护HTTPS连接的工厂
final CertificateChainCleaner certificateChainCleaner;//设置限制哪些证书受信任的证书
final HostnameVerifier hostnameVerifier;//注册认证的方式
final CertificatePinner certificatePinner;//反抓包
final Authenticator proxyAuthenticator;//返回一个请求,该请求包含满足{@code response}中的身份验证的凭据。如果无法满足条件,则返回null,默认没有
final Authenticator authenticator;//返回一个请求,该请求包含满足{@code response}中的身份验证的凭据。如果无法满足条件,则返回null。默认没有
final ConnectionPool connectionPool;//连接池 = 线程池 + 消息队列
final Dns dns;//DNS配置
final boolean followSslRedirects;//是否允许Ssl重定向,默认true
final boolean followRedirects;//是否允许重定向,默认true
final boolean retryOnConnectionFailure;//是否连接失败时重试,默认true
final int connectTimeout;//连接超时时间,默认10_000(10秒)
final int readTimeout;//读取超时时间,默认10_000(10秒)
final int writeTimeout;//写入超时时间,默认10_000(10秒)
final int pingInterval;//ping的时间间隔 默认 0
那么我们从OkHttpClient里面看到几个重要点:看一下能够调用的方法;发现到都是获取配置;并没有设置配置的接口;所以对应的OkHttpClient并没有直接提供对应的设置;
所以OkhttpClient初始化OkHttpClient(Builder builder) 借助于内部类Builder进行设置;
那么看一下这个内部类的builder的参数
Dispatcher dispatcher;
Proxy proxy;
List protocols;//设置默认的 http/2 ,http/1.1
List connectionSpecs;//TLS配置
//下面和上面的okhttp一样
final List interceptors = new ArrayList<>();
final List networkInterceptors = new ArrayList<>();
ProxySelector proxySelector;
CookieJar cookieJar;
Cache cache;
InternalCache internalCache;
SocketFactory socketFactory;
SSLSocketFactory sslSocketFactory;
CertificateChainCleaner certificateChainCleaner;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
ConnectionPool connectionPool;
Dns dns;
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int connectTimeout;
int readTimeout;
int writeTimeout;
int pingInterval;
初始化方法Builder()(这个是默认初始化的)和Builder(OkHttpClient okHttpClient)(可以将上次的okHttpClient设置的东西传递回去)
所以这个时候,先初始化一个builder类,再去重新设置该builder里面的部分方法;或者参数
那么我在builder里面挑出几个重要的点来讲吧;
2、首先Dispatcher这个类很重要;首先这个类干这几件事情:
1、创建非核心线程池executorService -》为之后的网络请求做异步处理;
2、设置了readyAsyncCalls队列(准备请求,开始网络请求数大于阀值64,以及不能有相同正在请求的host数量超过5个),这个其实就是缓存暂时不请求的接口;在请求完毕之后留在重新触发;
3、设置runningAsyncCalls队列;异步请求的;
4、设置了runningSyncCalls队列;同步请求的;
5、所以总的在请求数是runningAsyncCalls.size()+runningSyncCalls.size();
看一下图;里面的enqueue(AsyncCall call);添加进去做一个判断(异步操作)
(runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost)
runningAsyncCalls.add(call);//请求添加到队列
executorService().execute(call);//开始进行异步请求
如果添加在准备队列里面,当请求结束的时候;回调该方法
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();//是否将ready里面的值加到run里面(异步)
runningCallsCount = runningCallsCount();//获取请求的数量
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();//当无网络请求之后都会进行一次回调
}
}
这样的话,就完成了;一次网络请求;这个讲解的是异步以及是Dispatcher类;并不涉及真正的网络请求之类的;
下面在说明一下同步请求的方式:
这个就涉及到RealCall里面的execute()方法;会调用到.executed(this);然后添加进去
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
结束的时候也会调用finish的方法;这个就不涉及到是否进行最大值多少个之类的;来了就是请求;
我们现在来看一下请求的触发点;当然我们直讲重要的点RealCall;
/*
* Copyright (C) 2014 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import okhttp3.internal.NamedRunnable;
import okhttp3.internal.cache.CacheInterceptor;
import okhttp3.internal.connection.ConnectInterceptor;
import okhttp3.internal.connection.StreamAllocation;
import okhttp3.internal.http.BridgeInterceptor;
import okhttp3.internal.http.CallServerInterceptor;
import okhttp3.internal.http.RealInterceptorChain;
import okhttp3.internal.http.RetryAndFollowUpInterceptor;
import okhttp3.internal.platform.Platform;
import static okhttp3.internal.platform.Platform.INFO;
final class RealCall implements Call {
final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
/** The application's original request unadulterated by redirects or auth headers. */
final Request originalRequest;
final boolean forWebSocket;
// Guarded by this.
private boolean executed;
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
@Override public Request request() {
return originalRequest;
}
@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);
}
}
private void captureCallStackTrace() {
Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
}
@Override public void enqueue(Callback responseCallback) {//异步请求的方式
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));//触发异步请求;线程池
}
@Override public void cancel() {
retryAndFollowUpInterceptor.cancel();
}
@Override public synchronized boolean isExecuted() {
return executed;
}
@Override public boolean isCanceled() {
return retryAndFollowUpInterceptor.isCanceled();
}
@SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
@Override public RealCall clone() {
return new RealCall(client, originalRequest, forWebSocket);
}
StreamAllocation streamAllocation() {
return retryAndFollowUpInterceptor.streamAllocation();
}
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);
}
}
}
/**
* Returns a string that describes this call. Doesn't include a full URL as that might contain
* sensitive information.
*/
String toLoggableString() {
return (isCanceled() ? "canceled " : "")
+ (forWebSocket ? "web socket" : "call")
+ " to " + redactedUrl();
}
String redactedUrl() {
return originalRequest.url().redact();
}
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);
}
}
我们分别看一下请求的方式(异步和同步)
1、enqueue(Callback responseCallback) --异步方式
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));//触发runnable地方
}
final class AsyncCall extends NamedRunnable {
...
@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);
}
}
}
2、 Response 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);
}
}
所以重点是Response getResponseWithInterceptorChain()这个方法体;
我们现在去瞧瞧:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());//先添加client提供的拦截器,build里面才能添加;这个就是我们自定义的拦截器;这个是还没有请求的拦截器
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);//FIRST 触发
}
其中拦截器这个类是:
//拦截器都需要实现这个类
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;//其中proceed是触发下面一个类执行的条件
Connection connection();
}
}
其中我们还需要看一下RealInterceptorChain这类:
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
Connection 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);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);//设置下一个拦截器的Chain,从0开始
...
return response;
}
那么我们从FIRST开始看;触发了proceed;那么如果没有添加我们的拦截器;直接触发RetryAndFollowUpInterceptor拦截器;
public final class RetryAndFollowUpInterceptor implements Interceptor {
...
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);//复用和创建连接池的操作;这个在OkhttpClient里面实现方法;
int followUpCount = 0;
Response priorResponse = null;
while (true) {
...
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);//上面的代码实现;将连接池设置进去
...
}
...
}
这个创建连接池之后开始proceed;那么又调用RealInterceptorChain生成下一个拦截器的chain并且调用intercept
BridgeInterceptor填充cookie等,再次调用chain.proceed(requestBuilder.build());这个不需要在传递连接池;
然后在次触发CacheInterceptor,一样的再次触发ConnectInterceptor,CallServerInterceptor,触发之后在依次执行之前的proceed之后的方法;当然这个是继续执行;最终回到RetryAndFollowUpInterceptor里面执行followUpRequest然后返回response;
然后我们进行回调等处理;这样不管是异步还是同步;走的方式都是责任链的;
之后会继续讲解一下里面实现的细节;比如如何创建scoket等等