Android-OkHttp
基本使用
现在4.0以后基于Kotlin…
我们今天来看看3.14.2版,基于java…
kotlin目前博主不熟 - -!
添加依赖
implementation(“com.squareup.okhttp3:okhttp:3.14.2”)
代码使用
我们先来看看基本使用
建造者模式:一般在构建对象的过程需要配置很多很多的参数时,可以使用建造者模式来构建实体对象。
OkHttpClient client = new OkHttpClient();
//建造者模式:一般在
Request request = new Request.Builder()
.url(url)
.get() //默认为GET请求,可以不写
.build();
final Call call = client.newCall(request);
try {
//call.execute();//同步
call.enqueue(new Callback() { //异步
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
} catch (IOException e) {
e.printStackTrace();
}
源码分析
第一步:构建 OkHttpClient
第一步:我们先来看这个建造者模式在构建 OkHttpClient(new OkHttpClient)的时候做的事情
无参构造方法:内部自己new了一个Builder对象,并传入到了无修饰符的构造方法中;
有参构造方法:内部将个传入进来的Builder对象进行取值,并赋值到类中的成员变量中;
无修饰符的情况姑且称为default,访问范围是package,就是同一个包中的类可访问
....
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
....
public OkHttpClient() {
//在我们没有传入Builder对象的时候自己去new了一个,并传入到了下面这一个构造方法中
this(new Builder());
}
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = Util.platformTrustManager();
this.sslSocketFactory = newSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
if (sslSocketFactory != null) {
Platform.get().configureSslSocketFactory(sslSocketFactory);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.callTimeout = builder.callTimeout;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
if (interceptors.contains(null)) {
throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
}
}
我们再来看看OkHttpClient里面的Builder静态内部类
来看看Builder的两个构造方法
上面OkHttpClient构造方法中new了一个无参的Builer构造方法,这个无参构造方法里面进行了默认值的设置,上面有参构造方法中就讲这些默认参数设置到了自己的成员变量中
public static final class Builder {
//调度器
Dispatcher dispatcher;
//代理类:默认有三种代理模式DIRECT(直连),HTTP(http代理),SOCKS(SOCKS代理)
@Nullable Proxy proxy;
//协议集合,协议类,用来表示使用的协议版本,比如:http/1.0、http1.1、spdy/3.1、h2等
List<Protocol> protocols;
//连接规范,用于配置Socket连接层。对于Https,还能配置安全传输层协议(TLS)版本和密码套件
List<ConnectionSpec> connectionSpecs;
//拦截器,可以监听、重写和重试请求等
final List<Interceptor> interceptors = new ArrayList<>();
final List<Interceptor> networkInterceptors = new ArrayList<>();
EventListener.Factory eventListenerFactory;
//代理选择类,默认不适用代理,即使用直连方式,当前我们可以使用自定义指定URL使用某种代理,类似代理软件的PAC功能
ProxySelector proxySelector;
//Cookie 的保存获取
CookieJar cookieJar;
//缓存类,内部使用了DiskLruCache磁盘缓存来进行管理缓存
@Nullable Cache cache;
//内置缓存
@Nullable InternalCache internalCache;
//Socket的抽象创建工厂,通过createSocket来创建Socket
SocketFactory socketFactory;
//安全套接层工厂,HTTPS相关,用于创建SSLSocket,一般配置HTTPS证书
//对于不受信任的证书一般会提示 javax.net.ssl.SSLHandshakeException异常
@Nullable SSLSocketFactory sslSocketFactory;
@Nullable CertificateChainCleaner certificateChainCleaner;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
//连接池:我们通常将一个客户端和服务端和连接抽象为一个connection,
//而每一个connection都会被存放在ConnectionPool中,由它进行统一管理
//例如有一个相同的http请求产生时,connection就可以得到复用
ConnectionPool connectionPool;
//域名解析系统
Dns dns;
//是否遵循SSL重定向
boolean followSslRedirects;
//是否重定向
boolean followRedirects;
//失败是否重新连接
boolean retryOnConnectionFailure;
//回调超时时长
int callTimeout;
//连接超时时长
int connectTimeout;
//读取超时时长
int readTimeout;
//写入超时时长
int writeTimeout;
//与WebSocket有关,为了保持长连接,我们必须间隔一段时间发送一个ping指令进行保活
int pingInterval;
//无参的时候,做了一下默认值初始化
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
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;
callTimeout = 0;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
//有参OkHttpClient的时候,将这些值进行了赋值
Builder(OkHttpClient okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
this.protocols = okHttpClient.protocols;
this.connectionSpecs = okHttpClient.connectionSpecs;
this.interceptors.addAll(okHttpClient.interceptors);
this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
this.eventListenerFactory = okHttpClient.eventListenerFactory;
this.proxySelector = okHttpClient.proxySelector;
this.cookieJar = okHttpClient.cookieJar;
this.internalCache = okHttpClient.internalCache;
this.cache = okHttpClient.cache;
this.socketFactory = okHttpClient.socketFactory;
this.sslSocketFactory = okHttpClient.sslSocketFactory;
this.certificateChainCleaner = okHttpClient.certificateChainCleaner;
this.hostnameVerifier = okHttpClient.hostnameVerifier;
this.certificatePinner = okHttpClient.certificatePinner;
this.proxyAuthenticator = okHttpClient.proxyAuthenticator;
this.authenticator = okHttpClient.authenticator;
this.connectionPool = okHttpClient.connectionPool;
this.dns = okHttpClient.dns;
this.followSslRedirects = okHttpClient.followSslRedirects;
this.followRedirects = okHttpClient.followRedirects;
this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;
this.callTimeout = okHttpClient.callTimeout;
this.connectTimeout = okHttpClient.connectTimeout;
this.readTimeout = okHttpClient.readTimeout;
this.writeTimeout = okHttpClient.writeTimeout;
this.pingInterval = okHttpClient.pingInterval;
}
......
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
第二步:创建Request对象
第二步:创建Request对象,也是建造者模式构建出来的
new Request.Builder()的时候也是在初始化一些成员变量;
无参构造初始化了一个默认方法,一个请求头的Builder对象
有参构造的时候讲传入的参数进行赋值到成员变量中
public static class Builder {
@Nullable HttpUrl url;
String method;
Headers.Builder headers;
@Nullable RequestBody body;
Map<Class<?>, Object> tags = Collections.emptyMap();
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
Builder(Request request) {
this.url = request.url;
this.method = request.method;
this.body = request.body;
this.tags = request.tags.isEmpty()
? Collections.emptyMap()
: new LinkedHashMap<>(request.tags);
this.headers = request.headers.newBuilder();
}
public Builder url(HttpUrl url) {
if (url == null) throw new NullPointerException("url == null");
this.url = url;
return this;
}
public Builder url(String url) {
if (url == null) throw new NullPointerException("url == null");
// Silently replace web socket URLs with HTTP URLs.
if (url.regionMatches(true, 0, "ws:", 0, 3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
url = "https:" + url.substring(4);
}
return url(HttpUrl.get(url));
}
public Builder url(URL url) {
if (url == null) throw new NullPointerException("url == null");
return url(HttpUrl.get(url.toString()));
}
public Builder header(String name, String value) {
headers.set(name, value);
return this;
}
public Builder addHeader(String name, String value) {
headers.add(name, value);
return this;
}
public Builder removeHeader(String name) {
headers.removeAll(name);
return this;
}
public Builder headers(Headers headers) {
this.headers = headers.newBuilder();
return this;
}
public Builder cacheControl(CacheControl cacheControl) {
String value = cacheControl.toString();
if (value.isEmpty()) return removeHeader("Cache-Control");
return header("Cache-Control", value);
}
public Builder get() {
return method("GET", null);
}
public Builder head() {
return method("HEAD", null);
}
public Builder post(RequestBody body) {
return method("POST", body);
}
public Builder delete(@Nullable RequestBody body) {
return method("DELETE", body);
}
public Builder delete() {
return delete(Util.EMPTY_REQUEST);
}
public Builder put(RequestBody body) {
return method("PUT", body);
}
public Builder patch(RequestBody body) {
return method("PATCH", body);
}
public Builder method(String method, @Nullable RequestBody body) {
if (method == null) throw new NullPointerException("method == null");
if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
if (body != null && !HttpMethod.permitsRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must not have a request body.");
}
if (body == null && HttpMethod.requiresRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must have a request body.");
}
this.method = method;
this.body = body;
return this;
}
/** Attaches {@code tag} to the request using {@code Object.class} as a key. */
public Builder tag(@Nullable Object tag) {
return tag(Object.class, tag);
}
public <T> Builder tag(Class<? super T> type, @Nullable T tag) {
if (type == null) throw new NullPointerException("type == null");
if (tag == null) {
tags.remove(type);
} else {
if (tags.isEmpty()) tags = new LinkedHashMap<>();
tags.put(type, type.cast(tag));
}
return this;
}
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
}
第三步:创建Call对象
第三步:创建Call对象
final Call call = client.newCall(request)
newCall这个方法是OkHttpClient的方法
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
然后跟进RealCall.newRealCall去看看。
Call对象是个接口。
RealCall是Call对象的实现类,上面newRealCall返回的是这个Call的实现类。
newRealCall方法传入了OkHttpClient、Request对象、是否说使用WebSocket。
然后创建了RealCall对象,并返回了RealCall对象。
final class RealCall implements Call {
......
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.transmitter = new Transmitter(client, call);
return call;
}
}
......
第四步:发起请求(同步)
第四步:发起请求(同步)
execute():同步 一般用的比较少的…
getResponseWithInterceptorChain()方法的执行,我们留到后面来看,因为同步和异步最终都会调用这个方法…
只不过异步是创建了一个线程池,在线程池中执行了该方法的调用
接下来我们来看看这个同步的方法
因为Call是个接口,所以我们来看看他的实现类RealCall里面这个同步的方法实现;
@Override public Response execute() throws IOException {
//同步锁
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();
transmitter.callStart();
try {
//最终调用了OkHttpClient对象调度器(分发)的executed方法
client.dispatcher().executed(this);
//晚点我们再来看看这个方法的执行
return getResponseWithInterceptorChain();
} finally {
client.dispatcher().finished(this);
}
}
进入到调度器(分发)的executed方法看看;
将RealCall 对象传入进来,并添加到了队列集合当中;
到这一步这个executed方法就执行完了
//队列集合
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
synchronized void executed(RealCall call) {
//将我们这个RealCall对象添加到队列集合当中
runningSyncCalls.add(call);
}
第四步:发起请求(异步)
第四步:发起请求(异步)
enqueue():异步
接下来我们看看异步请求这个方法的调用
因为Call是个接口,所以我们来看看他的实现类RealCall里面这个异步的方法实现;
client.dispatcher().enqueue(new AsyncCall(responseCallback));
同样这里还是调用的调度器(分发)里面的方法;
但是这里调用的是异步的方法,并传入了OkHttp里面自定义的Runnable的接口实现类,并传入了我们自己实现的接口,等待回调;
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();
//创建了一个子线程,将传入进来的回调传入到子线程,调用调度器(分发)的enqueue方法,把这个穿件出来的子线程传入进去了
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
我们来看看AsyncCall这个在RealCall中的内部类NamedRunnable的子类
NamedRunnable 是实现了Runnable接口的,也就是创建了个子线程。
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
private volatile AtomicInteger callsPerHost = new AtomicInteger(0);
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
......
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));
再来看看这个调度器里面的enqueue方法
//子线程队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
void enqueue(AsyncCall call) {
synchronized (this) {
//将外面创建的子线程,添加到了子线程集合队列当中
readyAsyncCalls.add(call);
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
//调用了自己的方法
promoteAndExecute();
}
promoteAndExecute()来看看这个方法内部
将上面添加的子线程集合进行了过滤,放入到了局部子线程集合中
再次遍历过滤后的子线程集合,创建线程池传入到,子线程的executeOn方法中。
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
//创建了一个局部子线程集合
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
//拿到了子线程的迭代器,进行迭代
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
//超过了设置的最大请求数
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
//超过了设置的主机最大容量 host
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
asyncCall.callsPerHost().incrementAndGet();
//过滤掉了一些请求后,添加到了局部变量的子线程集合中去
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
//遍历过滤后的请求集合队列
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
//将创建出来的线程池传入到子线程的executeOn方法中
asyncCall.executeOn(executorService());
}
return isRunning;
}
//创建了线程池
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;
}
来看看AsyncCall .executeOn方法的执行;
记得上面调用时传入了一个创建的额线程池吗?
executeOn方法传入了线程池;
在这个方法内部调用了线程器的execute方法,进行执行了改子线程,也就会调用子线程的run方法
final class AsyncCall extends NamedRunnable {
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
//执行线程池,传入的是this,当前这个子线程,也就是会调用到当前这个子线程的run方法。
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
transmitter.noMoreExchanges(ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
}
父类(NamedRunnable )里面的run方法执行,
父类里面的run方法中,先获取了下当前的线程名,设置了新的线程名,然后执行了execute()抽象方法,最后又将旧的线程名设置回去了。
最终调用的是execute()抽象方法,我们还得回溯到子类当中去;
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();
}
AsyncCall 子类的execute抽象方法的实现…
final class AsyncCall extends NamedRunnable {
......
@Override protected void execute() {
boolean signalledCallback = false;
transmitter.timeoutEnter();
try {
//获取了Response 对象
Response response = getResponseWithInterceptorChain();
signalledCallback = true;
//还记得这个responseCallback吗? 这里也就是我们传入进来的CallBack实现...也就是在这里我们将Response回调出去了
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);
}
}
......
}
接下来我们看看getResponseWithInterceptorChain()这个方法;
还记得上面同步也调用了这个方法吗?
只不过上面的同步方法调用时没有在线程池中执行,执行完的结果直接return出去了,而这个异步是创建了线程池,在线程池中执行了这个方法…并将结果通过CallBack回调出去了…
getResponseWithInterceptorChain()
这个方法才是做出真正的响应,返回了Response对象;
1、创建了拦截器集合;
2、添加了各种拦截器(包括最先添加的用户自己定义的拦截器);
3、最终调用了 chain.proceed(originalRequest);得到一个Response 对象将其返回出去了;
等会儿在来看看proceed这个方法的调用
Response getResponseWithInterceptorChain() throws IOException {
// 创建了拦截器集合,添加了各种拦截器
List<Interceptor> interceptors = new ArrayList<>();
//用户添加的全局拦截器 也就是我们自己自定义的拦截器
interceptors.addAll(client.interceptors());
//错误、重定向拦截器
interceptors.add(new RetryAndFollowUpInterceptor(client));
//桥接拦截器,桥接应用层与网络层,添加必要的头
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//缓存处理的拦截器
interceptors.add(new CacheInterceptor(client.internalCache()));
//连接拦截器
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//通过okHttpClient.Builder#addNetworkInterceptor()传进来的拦截器只对非网页的请求生效
interceptors.addAll(client.networkInterceptors());
}
//真正访问服务器的拦截器
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
//获取到了 Response 对象;
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
//将其返回出去了
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
时序图
下面来看看上面同步和异步的时序图
拦截器(分析)
接下来我们来看看OkHttp里面的拦截器
Interceptor 是一个接口
既然是个接口,肯定在框架里面会有接口的实现
是否还记得getResponseWithInterceptorChain() 这个方法?
方法中创建了一个拦截器集合,往里面添加了很多拦截器…
package okhttp3;
......
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
OkHttp当中分为哪两类别?
1、应用拦截器addInterceptor():关注对象是request请求对象;
2、网络拦截器addNetworkInterceptor():关注的是发起请求,请求过程中,以及请求的数据之后;可以去做重定向,以及请求失败了去做重试;
在getResponseWithInterceptorChain()中添加完了拦截器集合之后
创建了一个RealInterceptorChain()拦截器,并将拦截器集合都传入进去了
RealInterceptorChain实现了implements Interceptor.Chain接口,
最终调用 Response response = chain.proceed(originalRequest);得到Response对象
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
Response response = chain.proceed(originalRequest);
上面通过RealInterceptorChain调用了proceed方法,我们跟进去看看proceed方法中的代码,里面各种判断,不符合的都会抛出异常;我们来看主要的代码;
这里面又new了一个自己,并将index+1,还记得是传入了拦截器集合吗?这里一个一个的将上面添加的拦截器取了出来,拦截器调用了intercept方法获取Response对象;
注:
结合拦截器的实现类中的intercept方法去看,这里是取出当前的拦截器的同时,准备new了个RealInterceptorChain对象,并将上一个拦截器获取到的数据传递到了下一个RealInterceptorChain对象中,方便在拦截器实现类中调用新的对象的proceed方法,拿到集合中的下一个拦截器。可以说是这里只是在做个标记,我这么理解
责任链模式
@Override public Response proceed(Request request) throws IOException {
return proceed(request, transmitter, exchange);
}
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
......
calls++;
......
// 自己创建了自己,然后将下表index+1;
//我们结合拦截器的实现类中的intercept方法去看,大概就明白了
//准备new了个RealInterceptorChain对象,并将上一个拦截器获取到的东西传递到了下一个RealInterceptorChain对象中,方便在拦截器实现类中调用新的对象的proceed方法,拿到集合中的下一个拦截器。可以说是这里只是在做个标记,我这么理解
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
//获取了集合中当前下标的拦截器
Interceptor interceptor = interceptors.get(index);
//拦截器调用了intercept方法,将刚刚new出来的自己传入进去了,获取到Response对象
Response response = interceptor.intercept(next);
......
return response;
}
既然interceptor拦截器是个接口,所以我们得去看看他的实现类
看看这个intercept方法的实现
看到中间这句 response = realChain.proceed(request, transmitter, null);
realChain记得是传入进来的new了新的RealInterceptorChain 方法,又调用了RealInterceptorChain 类中的proceed这个方法
public final class RetryAndFollowUpInterceptor implements Interceptor {
......
//刚刚是拦截器调用了intercept这个方法 外部RealInterceptorChain 的proceed方法中调用的,传入了一个新的RealInterceptorChain对象,记住是新的,创建时将index下标+了1
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Transmitter transmitter = realChain.transmitter();
int followUpCount = 0;
Response priorResponse = null;
while (true) {
transmitter.prepareToConnect(request);
if (transmitter.isCanceled()) {
throw new IOException("Canceled");
}
Response response;
boolean success = false;
try {
//又调用到了RealInterceptorChain 类中的proceed方法,记得吗?
//不过是新new的RealInterceptorChain 对象的proceed方法
response = realChain.proceed(request, transmitter, null);
success = true;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, transmitter, requestSendStarted, request)) throw e;
continue;
} finally {
// The network call threw an exception. Release any resources.
if (!success) {
transmitter.exchangeDoneDueToException();
}
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Exchange exchange = Internal.instance.exchange(response);
Route route = exchange != null ? exchange.connection().route() : null;
Request followUp = followUpRequest(response, route);
if (followUp == null) {
if (exchange != null && exchange.isDuplex()) {
transmitter.timeoutEarlyExit();
}
return response;
}
RequestBody followUpBody = followUp.body();
if (followUpBody != null && followUpBody.isOneShot()) {
return response;
}
closeQuietly(response.body());
if (transmitter.hasExchange()) {
exchange.detachWithViolence();
}
if (++followUpCount > MAX_FOLLOW_UPS) {
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
request = followUp;
priorResponse = response;
}
}
......
}
ConnectInterceptor 拦截器
我们大致来看一个拦截器ConnectInterceptor 拦截器
public final class ConnectInterceptor implements Interceptor {
public final OkHttpClient client;
public ConnectInterceptor(OkHttpClient client) {
this.client = client;
}
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
//先得到一个数据桥梁
Transmitter transmitter = realChain.transmitter();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//到这里就等于创建好了连接
Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);
//这里就可以说是往集合中的下一个拦截器中传递进去
return realChain.proceed(request, transmitter, exchange);
}
}
newExchange方法调用
先进行了同步锁,做了一些异常判断;
Exchange newExchange(Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
synchronized (connectionPool) {
if (noMoreExchanges) {
throw new IllegalStateException("released");
}
if (exchange != null) {
throw new IllegalStateException("cannot make a new request because the previous response "
+ "is still open: please call response.close()");
}
}
//获取到时HTTP1还是HTT2的链接方式
//对请求和响应去设置了各种信息,跟进去能看到设置了各种超时时间,在find这方法里面一步步跟进去
//会看到个RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
//一步步跟进去会进入到RealConnection类中的connect链接的方法
ExchangeCodec codec = exchangeFinder.find(client, chain, doExtensiveHealthChecks);
Exchange result = new Exchange(this, call, eventListener, exchangeFinder, codec);
synchronized (connectionPool) {
this.exchange = result;
this.exchangeRequestDone = false;
this.exchangeResponseDone = false;
return result;
}
}
通过上面调用这个转换器的find方法我们跟进来看看这个里面
获取一个有效的链接,尝试从复用池中去拿
final class ExchangeFinder {
//看名字是个连接池
private final RealConnectionPool connectionPool;
public ExchangeCodec find(
OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
//获取到超时一些超时设置
int connectTimeout = chain.connectTimeoutMillis();
int readTimeout = chain.readTimeoutMillis();
int writeTimeout = chain.writeTimeoutMillis();
int pingIntervalMillis = client.pingIntervalMillis();
boolean connectionRetryEnabled = client.retryOnConnectionFailure();
try {
//获取连接 核心代码 调用了自己内部的findHealthyConnection方法
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
return resultConnection.newCodec(client, chain);
} catch (RouteException e) {
trackFailure();
throw e;
} catch (IOException e) {
trackFailure();
throw new RouteException(e);
}
}
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
boolean doExtensiveHealthChecks) throws IOException {
while (true) {
//
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
pingIntervalMillis, connectionRetryEnabled);
// If this is a brand new connection, we can skip the extensive health checks.
synchronized (connectionPool) {
if (candidate.successCount == 0) {
return candidate;
}
}
// Do a (potentially slow) check to confirm that the pooled connection is still good. If it
// isn't, take it out of the pool and start again.
if (!candidate.isHealthy(doExtensiveHealthChecks)) {
candidate.noNewExchanges();
continue;
}
return candidate;
}
}
//获取到了一个有效的连接
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
boolean foundPooledConnection = false;
RealConnection result = null;
Route selectedRoute = null;
RealConnection releasedConnection;
Socket toClose;
//connectionPool 连接池
synchronized (connectionPool) {
if (transmitter.isCanceled()) throw new IOException("Canceled");
hasStreamFailure = false; // This is a fresh attempt.
// Attempt to use an already-allocated connection. We need to be careful here because our
// already-allocated connection may have been restricted from creating new exchanges.
releasedConnection = transmitter.connection;
toClose = transmitter.connection != null && transmitter.connection.noNewExchanges
? transmitter.releaseConnectionNoEvents()
: null;
if (transmitter.connection != null) {
// We had an already-allocated connection and it's good.
result = transmitter.connection;
releasedConnection = null;
}
if (result == null) {
// 尝试先从复用池里面去拿
if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, null, false)) {
foundPooledConnection = true;
result = transmitter.connection;
} else if (nextRouteToTry != null) {
selectedRoute = nextRouteToTry;
nextRouteToTry = null;
} else if (retryCurrentRoute()) {
selectedRoute = transmitter.connection.route();
}
}
}
closeQuietly(toClose);
if (releasedConnection != null) {
eventListener.connectionReleased(call, releasedConnection);
}
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
}
if (result != null) {
//这里是如果已经拿到了这个有效连接的话,我们就直接返回出去了
return result;
}
// If we need a route selection, make one. This is a blocking operation.
boolean newRouteSelection = false;
if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
newRouteSelection = true;
routeSelection = routeSelector.next();
}
List<Route> routes = null;
synchronized (connectionPool) {
if (transmitter.isCanceled()) throw new IOException("Canceled");
if (newRouteSelection) {
// Now that we have a set of IP addresses, make another attempt at getting a connection from
// the pool. This could match due to connection coalescing.
routes = routeSelection.getAll();
if (connectionPool.transmitterAcquirePooledConnection(
address, transmitter, routes, false)) {
foundPooledConnection = true;
result = transmitter.connection;
}
}
if (!foundPooledConnection) {
if (selectedRoute == null) {
selectedRoute = routeSelection.next();
}
// Create a connection and assign it to this allocation immediately. This makes it possible
// for an asynchronous cancel() to interrupt the handshake we're about to do.
result = new RealConnection(connectionPool, selectedRoute);
connectingConnection = result;
}
}
// If we found a pooled connection on the 2nd time around, we're done.
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
return result;
}
// 如果最终都没找到可以使用的链接的话,就自己去新增一次链接
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
connectionPool.routeDatabase.connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
connectingConnection = null;
// Last attempt at connection coalescing, which only occurs if we attempted multiple
// concurrent connections to the same host.
if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, routes, true)) {
// We lost the race! Close the connection we created and return the pooled connection.
result.noNewExchanges = true;
socket = result.socket();
result = transmitter.connection;
} else {
connectionPool.put(result);
transmitter.acquireConnectionNoEvents(result);
}
}
closeQuietly(socket);
eventListener.connectionAcquired(call, result);
return result;
}
}
看到这行 connectSocket(connectTimeout, readTimeout, call, eventListener);
调用了自己类中的connectSocket方法
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);最终调用的是Socket连接
public final class RealConnection extends Http2Connection.Listener implements Connection {
......
public void connect(int connectTimeout, int readTimeout, int writeTimeout,
......
connectSocket(connectTimeout, readTimeout, call, eventListener);
......
}
......
private void connectSocket(int connectTimeout, int readTimeout, Call call,
EventListener eventListener) throws IOException {
Proxy proxy = route.proxy();
Address address = route.address();
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);
eventListener.connectStart(call, route.socketAddress(), proxy);
//设置超时
rawSocket.setSoTimeout(readTimeout);
try {
//连接Socket
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
ce.initCause(e);
throw ce;
}
// The following try/catch block is a pseudo hacky way to get around a crash on Android 7.0
// More details:
// https://github.com/square/okhttp/issues/3245
// https://android-review.googlesource.com/#/c/271775/
try {
source = Okio.buffer(Okio.source(rawSocket));
sink = Okio.buffer(Okio.sink(rawSocket));
} catch (NullPointerException npe) {
if (NPE_THROW_WITH_NULL.equals(npe.getMessage())) {
throw new IOException(npe);
}
}
}
}
我们看看最后调用到Platform类中的connectSocket方法
public class Platform {
public void connectSocket(Socket socket, InetSocketAddress address, int connectTimeout)
throws IOException {
//Socket连接 address ip]端口都在里面
socket.connect(address, connectTimeout);
}
}
时序图
来看看这个连接拦截器的时序图
发现类好多,但是都是单一职责,方便进行扩展
复用池
我们来大致看下这个复用池
看看这里面创建了一个线程池
public final class RealConnectionPool {
//创建了一个线程池
private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize */,
Integer.MAX_VALUE /* maximumPoolSize */, 60L /* keepAliveTime */, TimeUnit.SECONDS,
new SynchronousQueue<>(), Util.threadFactory("OkHttp ConnectionPool", true));
}
复用池(线程池)
复用与一次响应过程不一样,一次响应式每次响应后都会去释放该链接;
复用池是在多长时间内,会进行复用,不用每次去重新打开链接,会去复用该链接;
阻塞时的连接,有链接过来时会被唤醒;
复用是需要ip和端口一样的情况下才会进行复用;
连接类型需要使用长连接Connection(请求头)
CallServerInterceptor拦截器
再来看看最后这个拦截器:interceptors.add(new CallServerInterceptor(forWebSocket));
最后执行到这个拦截器的时候,我们来看看它里面的intercept方法实现,最终他没有在调用proceed这个方法了吧
也就是到这个拦截器执行完的时候,最后通过所有拦截器的信息去获取最终的Response对象了,所以在添加连接器集合的时候这个是在最后添加的拦截器
来看看这个拦截器拦截的方法里面做了啥…
writeRequestHeaders写入了请求头
public final class CallServerInterceptor implements Interceptor {
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Exchange exchange = realChain.exchange();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
//写入请求头数据
exchange.writeRequestHeaders(request);
boolean responseHeadersStarted = false;
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
exchange.flushRequest();
responseHeadersStarted = true;
exchange.responseHeadersStart();
responseBuilder = exchange.readResponseHeaders(true);
}
if (responseBuilder == null) {
if (request.body().isDuplex()) {
// Prepare a duplex body so that the application can send a request body later.
exchange.flushRequest();
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, true));
request.body().writeTo(bufferedRequestBody);
} else {
// Write the request body if the "Expect: 100-continue" expectation was met.
BufferedSink bufferedRequestBody = Okio.buffer(
exchange.createRequestBody(request, false));
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
} else {
exchange.noRequestBody();
if (!exchange.connection().isMultiplexed()) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
exchange.noNewExchangesOnConnection();
}
}
} else {
exchange.noRequestBody();
}
if (request.body() == null || !request.body().isDuplex()) {
exchange.finishRequest();
}
if (!responseHeadersStarted) {
exchange.responseHeadersStart();
}
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
if (code == 100) {
// server sent a 100-continue even though we did not request one.
// try again to read the actual response
response = exchange.readResponseHeaders(false)
.request(request)
.handshake(exchange.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
exchange.responseHeadersEnd(response);
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(exchange.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
exchange.noNewExchangesOnConnection();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
}
public final class Exchange {
//写入请求头数据 上面这个拦截器调用的方法
public void writeRequestHeaders(Request request) throws IOException {
try {
//监听回调 开始写入请求头
eventListener.requestHeadersStart(call);
//这个codec是最终的请求方式吧,对吧
// HTTP1 HTT2 写入请求头
codec.writeRequestHeaders(request);
//回调监听,请求头数据写入完成了
eventListener.requestHeadersEnd(call, request);
} catch (IOException e) {
eventListener.requestFailed(call, e);
trackFailure(e);
throw e;
}
}
}
看到没有,这个请求方式调用写入的方法,区分了http1、http2
这个是Http1里面最终拼接请求头的方法
/**
*
*Http1 的方式
*/
public final class Http1ExchangeCodec implements ExchangeCodec {
......
private final BufferedSink sink;
......
//写入请求头请求数据
public void writeRequest(Headers headers, String requestLine) throws IOException {
if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
sink.writeUtf8(requestLine).writeUtf8("\r\n");
for (int i = 0, size = headers.size(); i < size; i++) {
sink.writeUtf8(headers.name(i))
.writeUtf8(": ")
.writeUtf8(headers.value(i))
.writeUtf8("\r\n");
}
sink.writeUtf8("\r\n");
state = STATE_OPEN_REQUEST_BODY;
}
//发射数据 记得Socket的发射数据吗flush
@Override public synchronized void flush() throws IOException {
if (closed) return; // Don't throw; this stream might have been closed on the caller's behalf.
//最终肯定是调用了OutputStream.flush()
sink.flush();
}
}
这里是HTTP2拼接请求头的方法
@Override public void writeRequestHeaders(Request request) throws IOException {
if (stream != null) return;
boolean hasRequestBody = request.body() != null;
List<Header> requestHeaders = http2HeadersList(request);
stream = connection.newStream(requestHeaders, hasRequestBody);
// We may have been asked to cancel while creating the new stream and sending the request
// headers, but there was still no stream to close.
if (canceled) {
stream.closeLater(ErrorCode.CANCEL);
throw new IOException("Canceled");
}
stream.readTimeout().timeout(chain.readTimeoutMillis(), TimeUnit.MILLISECONDS);
stream.writeTimeout().timeout(chain.writeTimeoutMillis(), TimeUnit.MILLISECONDS);
}
RealConnection这个对象是有效连接,前面一系列操作就是为了得到这个有效链接,尝试从复用池去获取,如果没有,就会去创建新的Socket链接
HttpURLConnection是用的Socket;
OkHttp里面也是使用Socket来发送请求,但是他跳过了Android封装的HttpURLConnection;