定义个人解读
OkHttp is HTTP client for Java and Kotlin.
定义其一是说OKHttp是为Java和Kotlin(4.x版本已经用Kotlin重写)平台打造的高效网络框架,自不必多说;其二OkHttp是Http 客户端,这里面包含的知识就很多了:
-
专注于Http协议,不适用于其他协议;专注于客户端Http通信需求,不适用于服务端。
-
支持所有的Http协议版本,意味着支持所有的Http协议通信。OkHttp帮我们关注了Http世界动态,HTML5新协议WebSocket同样支持。
注 文章参考版本 ‘3.14.7’ 不再支持 http/1.0 和 SPDY_3。
HTTP_1_0(“http/1.0”)
HTTP_1_1(“http/1.1”)
SPDY_3(“spdy/3.1”)
HTTP_2(“h2”)
H2_PRIOR_KNOWLEDGE(“h2_prior_knowledge”)
QUIC(“quic”)
- 支持多种SSL/TLS版本,意味Https也不在话下。
TLS_1_3(“TLSv1.3”), // 2016.
TLS_1_2(“TLSv1.2”), // 2008
TLS_1_1(“TLSv1.1”), // 2006
TLS_1_0(“TLSv1”), // 1999
SSL_3_0(“SSLv3”), // 1996
我个人觉得,OkHttp可以理解为一个小型浏览器(除去界面渲染功能),在地址栏输入URL到返回结果,中间过程涉及很多动作,如缓存、延迟、安全验证、连接、DNS解析、cookie等等。OkHttp能够做到使用它好比像普通用户使用浏览器一样简单,真是很了不起。所以OkHttp不仅仅是一个框架而已,更是Http协议甚至是TCP/IP协议的最佳实战基地。理解它,个人觉得需要扎实的网络协议基础知识,
推荐一个书单:
顺便提一下,Okhttp 整个工程除去test代码,实际不到3万代码,相当于一个中等规模的APP。
六个问题读源码
首先看一下OKhttp的大致流程:
对网络进行配置有两个地方,一是OKHttpClient全局配置(自定义Interceptor等),二是Request配置(缓存策略等)使不同Request可以具有不同策略。
OkHttpClient配置:
public Builder() {
dispatcher = new Dispatcher();//异步Call执行调度
protocols = DEFAULT_PROTOCOLS;//Http 协议
connectionSpecs = DEFAULT_CONNECTION_SPECS;// SSL/TLS 配置
eventListenerFactory = EventListener.factory(EventListener.NONE);//Events 事件
proxySelector = ProxySelector.getDefault();// 代理
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
cookieJar = CookieJar.NO_COOKIES;// Cookie 配置
socketFactory = SocketFactory.getDefault();//Socket 构造工厂
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;
}
Request配置:
Builder(Request request) {
this.url = request.url;//请求URL
this.method = request.method;//Http 方法
this.body = request.body;// Http 请求体
this.tags = request.tags.isEmpty()
? Collections.emptyMap()
: new LinkedHashMap<>(request.tags);//Tag标记
this.headers = request.headers.newBuilder();// Http请求头
}
Interceptor链
Application Interceptor 和 NetworkInterceptor的区别一目了然:一个参与RetryAndFollowUpInterceptor 重试和重定向循环,一个不参与,且为第一个拦截器,只对最终response起作用。
List<Interceptor> interceptors = new ArrayList<>();
// 添加自定义Application Interceptor
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));
// 添加自定义NetworkInterceptor,如果为WebSocket链接,自定义NetworkInterceptor无法起作用
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
上述代码显示了Interceptors的前后顺序,值得注意一点的是,如果是WebSocket链接,自定义NetworkInterceptor将无法起作用。关于各个Interceptor的源码详解,其他Blog写的特别棒,大伙自行查询。
这里说一下ConnectInterceptor的ConnectionPool(连接池)。ConnectionPool是对Connection对象的管理,属于对象池。
Background threads are used to cleanup expired connections. There will be at most a single thread running per connection pool. The thread pool executor permits the pool itself to be garbage collected.
一:后台线程主要作用是garbage collected(垃圾回收);二:最多只有一个线程。
自定义Interceptor以及LoggingInterceptor打印的是什么
通过上文,理解两种自定义Interceptor的关键是其在Interceptor链中的位置。每种Interceptor拦截处理以Chain.process()方法为分水岭,之前可以对Request进行拦截,之后对Response拦截,只有调用Chain.process方法才会进行下一个拦截器处理。Application Interceptor 处于Interceptor链第一个且不参与重试和重定向,所以只关心最后的response,并且有权利去决定是否使用Okhttp默认Interceptor,当然如果不使用而是重写Interceptor链,Okhttp也没有必要引用。NetWork Interceptor处于客户端与服务端建立链接和通信之间,且参与重试和重定向,所以可以得到更多Request头部以及网络链接过程信息。LoggingInterceptor打印的是什么,答案不言自明。根据自己想要的信息,将LoggingInterceptor放置在Interceptor链中的相应位置。
如何配置协议
默认协议配置DEFAULT_PROTOCOLS = Util.immutableList(Protocol.HTTP_2, Protocol.HTTP_1_1);
协议配置代码:
public Builder protocols(List<Protocol> protocols) {
protocols = new ArrayList<>(protocols);
// H2_PRIOR_KNOWLEDGE 和 HTTP_1_1必须配置其一。
if (!protocols.contains(Protocol.H2_PRIOR_KNOWLEDGE)
&& !protocols.contains(Protocol.HTTP_1_1)) {
throw new IllegalArgumentException(
"protocols must contain h2_prior_knowledge or http/1.1: " + protocols);
}
// 如果配置H2_PRIOR_KNOWLEDGE,不在允许配置其他协议版本。
if (protocols.contains(Protocol.H2_PRIOR_KNOWLEDGE) && protocols.size() > 1) {
throw new IllegalArgumentException(
"protocols containing h2_prior_knowledge cannot use other protocols: " + protocols);
}
// 不再支持HTTP_1_0
if (protocols.contains(Protocol.HTTP_1_0)) {
throw new IllegalArgumentException("protocols must not contain http/1.0: " + protocols);
}
if (protocols.contains(null)) {
throw new IllegalArgumentException("protocols must not contain null");
}
// 不再支持SPDY_3
protocols.remove(Protocol.SPDY_3);
this.protocols = Collections.unmodifiableList(protocols);
return this;
}
有一点需要说明的是H2_PRIOR_KNOWLEDGE是不能和Https一起使用的。
// RealConnection.connect方法
if (route.address().protocols().contains(Protocol.H2_PRIOR_KNOWLEDGE)) {
throw new RouteException(new UnknownServiceException(
"H2_PRIOR_KNOWLEDGE cannot be used with HTTPS"));
}
缓存怎么用
缓存策略配置是在Request端,所以不同Request可以具有不同的缓存策略。
另外,Cache类为缓存仓库,负责存入与取出;CacheStrategy类仓库管理者,负责缓存策略门卡检验。
// CacheInterceptor.intercept方法
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request request = new Request.Builder()
.url(url)
//noCache(): 就算是本地有缓存,也不会读缓存,直接访问服务器
//noStore(): 不会缓存数据,直接访问服务器
//onlyIfCached():只请求缓存中的数据,不靠谱
.cacheControl(new CacheControl.Builder().build())
.build();
非常棒的一篇Blog,https://blog.csdn.net/briblue/article/details/52920531
Https如何配置
官方代码示例,https://square.github.io/okhttp/https/
Event事件机制
官方代码示例,https://square.github.io/okhttp/events
Help
资源
如需文章中书籍与视频资源,@邮箱zhckogx@163.com