implementation 'com.squareup.okhttp3:okhttp:3.11.0'
背景
之前的底层网络库基本就是Apache HttpClient和HttpURLConnection。由于HttClient比较难用,官方在Android2.3以后就不建议用了,并且在Android5.0以后废弃了HttpClient,在Android6.0更是删除了HttpClient。
HttpURLConnection是一种多用途、轻量极的HTTP客户端,使用它来进行HTTP操作可以适用于大多数的应用程序,但是在Android 2.2版本之前存在一些bug,所以官方建议在Android2.3以后替代HttpClient,Volley就是按版本分区使用这两个网络库。
然而随着开源届扛把子Square的崛起,OkHttp的开源,这两个网络库只能被淹没在历史洪流中。Android4.4以后HttpURLConnection的底层已经替换成OkHttp实现。OkHttp配合同样是Square开源的Retrofit,网络请求变得更简便,功能更强大。
介绍
OkHttp是一个现代,快速,高效的网络库,OkHttp 库的设计和实现的首要目标是高效。
1.支持 HTTP/2和SPDY,这使得对同一个主机发出的所有请求都可以共享相同的套接字连接;
2.如果 HTTP/2和SPDY不可用,OkHttp会使用连接池来复用连接以提高效率。
3.支持Gzip降低传输内容的大小
4.支持Http缓存
以上均是摘抄别人的东西
简单使用
//创建OkHttpClient 对象
OkHttpClient client = new OkHttpClient();
//创建request
Request request = new Request.Builder()
.url("")
.build();
//创建请求对象
Call call = okHttpClient.newCall(request);
//发起同步请求
try {
Response execute = call.execute();
} catch (Exception e) {
e.printStackTrace();
}
//发起异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
源码分析
第一步:创建OkHttpClient对象
OkHttpClient client = new OkHttpClient();
//另一种方法是通过Builder创建
OkHttpClient.Builder builder = new OkHttpClient.Builder();
//可以设置一些builder,如设置连接超时时间
builder.connectTimeout(5000, TimeUnit.SECONDS);
//初始化OkhttpClient完成
OkHttpClient okHttpClient = builder.build();
首先我们点击创建的 OkHttpClient 对象进去源码是这样的:
public OkHttpClient() {
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.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 = systemDefaultTrustManager();
this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
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.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
}
可以看到有很多常量,这里使用了建造者模式,所以这些常量可以通过 build() 进行配置。如果不进行配置则使用无参构造中传进来的默认配置,每个常量的意思具体如下:
public Builder() {
// 分发器Dispatcher对象
dispatcher = new Dispatcher();
// 协议集合,默认协议集合包括HTTP2,HTTP_1_1
protocols = DEFAULT_PROTOCOLS;
// 传输层版本和连接协议
connectionSpecs = DEFAULT_CONNECTION_SPECS;
// 代理选择器
proxySelector = ProxySelector.getDefault();
// Cookie瓶,为HTTP的cookies提供策略和持久化
cookieJar = CookieJar.NO_COOKIES;
// socket工厂类
socketFactory = SocketFactory.getDefault();
// 主机名字确认
hostnameVerifier = OkHostnameVerifier.INSTANCE;
// 证书链
certificatePinner = CertificatePinner.DEFAULT;
// 代理服务器身份验证
proxyAuthenticator = Authenticator.NONE;
// 源服务器身份验证
authenticator = Authenticator.NONE;
// 连接池
connectionPool = new ConnectionPool();
// 域名
dns = Dns.SYSTEM;
// 是否遵循 ssl 重定向
followSslRedirects = true;
// 是否遵循重定向
followRedirects = true;
// 连接失败的时候是否重试
retryOnConnectionFailure = true;
// 连接超时
connectTimeout = 10_000;
// 读取超时
readTimeout = 10_000;
// 写入超时
writeTimeout = 10_000;
// HTTP / 2 和 Web 套接字 ping 之间的时间间隔
pingInterval = 0;
}
第二步:创建Request对象
Request request = new Request.Builder()
.url("")
.build();
可以看到,这里同样使用了建造者模式,我们点击 Request 进去看看:
final HttpUrl url;
final String method;
final Headers headers;
final RequestBody body;
final Object tag;
Request只是用来设置一些请求链接(url)、请求方法(method)、请求头(headers)、请求体(body)、标签(tag,可作为取消请求的标记)。
第三步:创建请求对象Call
Call call = okHttpClient.newCall(request);
首先我们点进newCall()方法里去看一下:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
发现调用的的是RealCall类里面的newRealCall()方法,并传入了了okttpclient和request。
再跟进到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;
}
发现是创建了一个 RealCall 对象,并返回给上一层。RealCall 是 Call 的实现类,Call 定义了请求相关的操作,例如同步异步、取消请求等方法。所以后续的请求相关操作基本都是在调用 Call 定义的方法,而这些方法真正的执行是它的实现类 RealCall。
最后看看 RealCall 的构造函数,该函数是比较简单的,只是赋值一些常量,然后创建了重试与重定向拦截器(RetryAndFollowUpInterceptor)(这个后面会讲):
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
到这之后我们就可以去发起请求了,像上面的简单使用,可以发起同步请求execute(),或者发起异步请求enqueue(),下面首先说一下同步请求,比较简单,之后再说异步请求。
第四步:同步请求
Response execute = call.execute();
点进execute()方法进去看一下都做了什么东西:
@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);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
首先我们先看一下刚进入方法的这一个判断,尔值executed表示一个okhttp请求只能运行执行一次
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
接着往下看真正调用请求的是这个方法,分发器的excuted()方法:
client.dispatcher().executed(this);
点进去会发现调的是Dispatcher类executed()方法,先来看一下Dispatcher,主要是有以下里面的东西:
//最大请求数是64
private int maxRequests = 64;
//最大主机连接数是5
private int maxRequestsPerHost = 5;
//线程池
private @Nullable ExecutorService executorService;
//准备执行的异步任务队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//正在执行的异步任务队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//正在执行的同步任务队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
看完Dispatcher类,继续来看executed()方法:
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
里面的add()方法是将同步请求任务添加到同步请求队列中。
下面就到了最重要的一步,获取请求结果的方法:
Response result = getResponseWithInterceptorChain();
进入到方法里去看一下:
Response getResponseWithInterceptorChain() throws IOException {
// 创建一个拦截器集合,
List<Interceptor> interceptors = new ArrayList<>();
//添加用户自定义的interceptors
interceptors.addAll(client.interceptors());
//添加retryAndFollowUpInterceptor拦截器,主要负责请求失败重试
interceptors.add(retryAndFollowUpInterceptor);
/**
* 添加桥拦截器,设置请求头内容类型、长度、编码。添加cookie,设置其他报头,如User-Agent,Host,Keep-alive等。其中
* Keep-Alive是实现多路复用的必要步骤设置gzip压缩,并在接收到内容后进行解压。省去了应用层处理数据解压的麻烦
*/
interceptors.add(new BridgeInterceptor(client.cookieJar()));
// 添加缓存拦截器,主要负责cache管理
interceptors.add(new CacheInterceptor(client.internalCache()));
// 添加连接拦截器,负责获取连接
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
// 添加用户自定义的网络拦截器
interceptors.addAll(client.networkInterceptors());
}
// 添加服务器请求拦截器,负责向服务器发起真正的网络请求,并接收到服务器响应以后返回请求结果。
interceptors.add(new CallServerInterceptor(forWebSocket));
// 构建责任链,通过拦截器构造Interceptor.Chain,其真正实现是RealInterceptorChain
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
// 处理责任链中的拦截器,通过拦截器链的proceed()方法获取请求结果
return chain.proceed(originalRequest);
}
可以看到,这里用到了很多拦截器,将这些拦截器构建成一条责任链,然后再一个个处理。这里用到了责任链模式,每个拦截器负责相应的功能,上一个拦截器完成会传给下一个拦截器,直到最后一个拦截器执行完再一层层向上返回 Response。
接下来点进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, 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;
}
其他的一些判断就不看了,主要看一下这个:
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);
这里会构建一个新的责任链,然后把责任链的索引加 1(为了下次从拦截器集合中取出下一个拦截器),接着从拦截器集合中取出当前拦截器并调用 intercept() 方法,这样如果这个拦截器可以完成任务会马上返回 Response,否则会在 intercept() 方法中继续处理责任链,因为该 intercept() 方法中会继续调用责任链的 proceed() 方法。通过责任链模式,循环调用,最终获取请求结果。
获取结果后还会去调用finished()方法,是在在finally中执行的:
client.dispatcher().finished(this);
点进去是这样的:
void finished(RealCall call) {
//注意传递的是哪个队列,注意boolen值,一个是同步用到的,一个是异步用到的
finished(runningSyncCalls, call, false);
}
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!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
calls.remove(call) 只是把当前 RealCall 从正在运行的同步请求队列中移除了,说明请求已经完成了。
到这里同步请求的分析就已经完了,下面分析一下异步请求的流程。
第五步:异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
进到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));
}
前几步和同步请求的一样,我们只需要关注一行代码就可以了:
client.dispatcher().enqueue(new AsyncCall(responseCallback));
下面我们点进去这个方法里面看一下:
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;
}
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();
/**
* 判断重定向和重试拦截器是否取消,如果取消,调用responseCallback的onFailure回调,responseCallback就是我们通过
* enqueue方法传入的Callback对象。如果没取消,调用responseCallback的onResponse回调。
*/
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);
}
}
}
我们发现AsyncCall继承自NamedRunnable,而NamedRunnable实现了Runnable,并且是在NamedRunnable执行了run方法,在这个run()方法中执行了excute()方法也就是AsyncCall中的那个excute()方法。这个excute()方法就不多说了,就是获取请求结果,相信大家一定能看的懂。最后讲一下这个方法:
client.dispatcher().finished(this);
点进去看一下和同步请求的有什么不同:
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!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
finished方法内部会将本次的异步请求RealCall从正在请求的异步请求队列中移除,由于promoteCalls传入的是true,接着调用promoteCalls()方法。
进到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(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
这个里面做的就是,循环从readyAsyncCalls中取出任务,然后将该任务放到runningAsyncCalls中,最后再调用调用executorService().execute(call)执行任务。
到这里就结束了,历时一周,经过东拼西凑终于是写完了这篇帖子,发现问题的,大家可以留在评论里指出,我在进行修改。
最后,感觉对大家有用的,可以点一下关注,会不定时分享一些安卓相关的知识点。