OkHttp之ConnectInterceptor简单分析
在《 Okhttp之CacheInterceptor简单分析 》这篇博客中简单的分析了下缓存拦截器的工作原理,通过此博客我们知道在执行完CacheInterceptor之后会执行下一个浏览器——ConnectInterceptor,本篇就对此拦截器的工作做简单的梳理,Connect顾名思义该拦截器的主要作用是打开了与服务器的链接,正式开启了网络请求,还是从intercept方法说起:
public Response intercept(Chain chain) throws IOException {
。。。。。
//从拦截器链里得到StreamAllocation对象
StreamAllocation streamAllocation = realChain.streamAllocation();
。。。。
HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
//获取realConnetion
RealConnection connection = streamAllocation.connection();
//执行下一个拦截器
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
从代码上来看该拦截器的主要功能都交给了StreamAllocation处理,且这个类是从拦截器链对象(RealInterceptorChain对象)上获取的,通过《Okhttp源码简单解析(一) 》这篇博客我们知道RealInterceptorChain是在RealCall的getResponseWithInterceptorChain方法初始化的
Response getResponseWithInterceptorChain() throws IOException {
//省略部分与本篇博客无关的代码
//将拦截器集合交给RealInterceptorChain这个Chain对象来处理
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
RealInterceptorChain构造函数第二个参数就是我们需要的StreamAllocation对象,但是,纳尼?为什么上面创建 RealInterceptorChain对象的时候第二个参数传的null?
其实,根据《Okhttp源码简单解析(一) 》这篇博客我们知道拦截器链的工作原理,且Okhttp内置拦截器链上第一个拦截器就是RetryAndFollowUpInterceptor,事实上StreamAllocation 就是在这个拦截器里面初始化的!
private StreamAllocation streamAllocation;
Response intercept(Chain chain) throws IOException {
//初始化streamAllocation对象
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
//执行procced方法,将streamAllocation对象传给拦截器链
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
}
可以看出在RetryAndFollowUpInterceptor这个拦截器会初始化一个StreamAllocation交给拦截器链(也就是说一次发起一次请求就会生成一个StreamAllocation对象,但是StreamAllocation对象公用一个连接池)。那么这个StreamAllocation到底是神马玩意?该构造器里面有两个重要的参数:
1.使用了Okhttp的连接池ConnectionPool
2.通过url创建了一个Address对象。
Okhttp连接池简单说明:
本篇只是对连接池做最简单的说明,内部的实现原理暂时不细讲。在Okhttp内部的连接池实现类为ConnectionPool,该类持有一个ArrayDeque队列作为缓存池,该队列里的元素为RealConnection(通过这个名字应该不难猜出RealConnection是来干嘛的)。
该链接池在初始化OkhttpClient对象的时候由OkhttpClient的Builder类创建,并且ConnectionPool提供了put、get、evictAll等操作。但是Okhttp并没有直接对连接池进行获取,插入等操作;而是专门提供了一个叫Internal的抽象类来操作缓冲池:比如向缓冲池里面put一个RealConnection,从缓冲池get一个RealConnection对象,该类里面有一个public且为static的Internal类型的引用:
//抽象类
public abstract class Internal {
public static Internal instance;
}
instance的初始化是在OkhttpClient的static语句块完成的:
static {
Internal.instance = new Internal() {
//省略部分代码
};
}
在文章开头ConnectionInterceptor的intercept方法中拿到StreamAllocation对象之后就调用了newStream方法,还是先说说这个方法是干什么的吧,然后在分析其源码(带着结论讲解源码能方便的说明问题),newStream方法主要做了工作:
1)从缓冲池ConnectionPool获取一个RealConnection对象,如果缓冲池里面没有就创建一个RealConnection对象并且放入缓冲池中,具体的说是放入ConnectionPool的ArrayDeque队列中。
2)获取RealConnection对象后并调用其connect**打开Socket链接**
下面就分析其源码:
public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
。。。
//获取一个RealConnection对象
RealConnection resultConnection = findHealthyConnection(。。。);
//获取HttpCodec 对象
HttpCodec resultCodec = resultConnection.newCodec(client, this);
//返回HttpCodec对象
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
}
仅从上面的代码来看该方法主要就是做了两件事:
1、调用findHealthyConnection获取一个RealConnection对象。
2、通过获取到的RealConnection来生成一个HttpCodec对象并返回之。
所以,看看findHealthyConnection做了神马?
private RealConnection findHealthyConnection(。。。) {
while (true) {//一个循环
//获取RealConnection对象
RealConnection candidate = findConnection(。。。);
synchronized (connectionPool) {
//直接返回之
if (candidate.successCount == 0) {
return candidate;
}
}
//对链接池中不健康的链接做销毁处理
if (!candidate.isHealthy(doExtensiveHealthChecks)) {
noNewStreams();
continue;
}
//返回
return candidate;
}//end while
}
上面代码也很简单:
1、开启一个while循环,调用findConnection继续获取RealConnection对象candidate 。
2、如果candidate 的successCount 为0,直接返回之,while循环结束
3、如果candidate是一个不“健康”的对象,则对此对象进行调用noNewStreams进行销毁处理,继续循环调用findConnection获取RealConnection对象。
(注:不健康的RealConnection条件为如下几种情况:
RealConnection对象 socket没有关闭
socket的输入流没有关闭
socket的输出流没有关闭
http2时连接没有关闭
)
所以继续看看findConnection方法做了些神马?
private RealConnection findConnection(。。。){
//选中的路由
Route selectedRoute;
synchronized (connectionPool) {
。。。。
//尝试复用
RealConnection allocatedConnection =this.connection;
//可以复用直接返回
if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
return allocatedConnection;
}
//从连接池获取一个连接,此时
Internal.instance.get(connectionPool, address, this, null);
//成功从连接池中获取一个连接,返回之
if (connection != null) {
return connection;
}
//当前对象使用的路由对象
selectedRoute = route;
}
// If we need a route, make one. This is a blocking operation.
if (selectedRoute == null) {//阻塞操作
selectedRoute = routeSelector.next();
}
RealConnection result;
synchronized (connectionPool) {
//从缓冲池中获取对象
Internal.instance.get(connectionPool, address, this, selectedRoute);
//缓存池中有此连接
if (connection != null) return connection;
//当前对象新的路由对象
route = selectedRoute;
refusedStreamCount = 0;
//缓存池中没有此连接,初始化一个 ,
result = new RealConnection(connectionPool, selectedRoute);
//将当前StreamAllocation的弱引用
//交给result的allocations集合里
//并将result赋值给this.connection这个引用
acquire(result);
}
//开始Socket连接,为阻塞操作
result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
routeDatabase().connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
// 将新创建的RealConection放入到缓冲池
Internal.instance.put(connectionPool, result);
// If another multiplexed connection to the same address was created concurrently, then
// release this connection and acquire that one
//如果另外的多路复用连接在同时床架,则释放此连接,用另外的链接
if (result.isMultiplexed()) {//此处代码暂不分析
socket = Internal.instance.deduplicate(connectionPool, address, this);
result = connection;
}
}
closeQuietly(socket);
return result;
}
结合代码中的注释,这个类大体上做了如下几个工作:
1、StreamAllocation的connection能复用就复用之
2、如果connection不能复用,则从连接池中获取RealConnection,获取成功则返回,从连接池中获取RealConnection的方法调用了两次
,第一次没有传Route,第二次传了(这个坑以后讲解)
3,如果连接池里没有则new一个RealConnection对象,并放入连接池中
4.最终调用RealConnection的connect方法打开一个socket链接(此处暂且说结论,至于为何断定是socket链接,篇幅有限另外结合别的知识点另开博文说明)。
到此为止,ConnectionInterceptor简单分析完毕,分析了这么多,总的来说ConnectionInterceptor就是弄一个RealConnection对象,然后创建Socket链接,并且调用下一个也是最后一个拦截器来完成Okhttp的整个操作。
限于篇幅原因,虽然还有好多知识点和要点没有讲解,但是为了本篇博客的主题不至于跑偏,本篇博客就此完结,关于里面涉及的另外的知识点和要点会另外写博客专门梳理,如有不当之处,欢迎批评指正
OkHttp之ConnectInterceptor简单分析的更多相关文章
- Okhttp之CallServerInterceptor简单分析
在Okhttp源码分析专栏的几篇博客分析了Okhttp几个拦截器的主要功能,还剩下最后一个拦截器CallServerInterceptor没有分析,本篇博客就简单分析下该拦截器的功能. 在Okhttp ...
- Okhttp之CacheInterceptor简单分析
<OkHttp之BridgeInterceptor简单分析 >简单分析了BridgeInterceptor的工作原理,在Okhttp的拦截器链上BridgeInterceptor的下一个拦 ...
- OkHttp之BridgeInterceptor简单分析
在< Okhttp源码简单解析(一) >这篇博客简单分析了Okhttp请求的执行流程,通过该篇博客我们知道OkHttp的核心网络请求中内置"拦截器"发挥了重大作用:本篇 ...
- Okhttp之RealConnection建立链接简单分析
在之前的博客中我们知道Okhttp在发起链接请求先从链接池中获取连接,如果链接池中没有链接则创建新的链接RealConnection对象,然后执行其connet方法打开SOCKET链接(详见< ...
- Okhttp之连接池ConnectionPool简单分析(一)
开篇声明:由于本篇博文用到的一些观点或者结论在之前的博文中都已经分析过,所以本篇博文直接拿来用,建议读此博文的Monkey们按照下面的顺序读一下博主以下博文,以便于对此篇博文的理解: <Okht ...
- Okhttp对http2的支持简单分析
在< Okhttp之RealConnection建立链接简单分析>一文中简单的分析了RealConnection的connect方法的作用:打开一个TCP链接或者打开一个隧道链接,在打开t ...
- Okhttp之RouteSelector简单解析
继前面的几篇OKhttp的拦截器简单分析之后,对于后续Okhttp之间的分析自己也着实琢磨了一段时间,是分析RealConnection?还是ConnectionPool,随着对Okhttp源码的深入 ...
- 简单分析JavaScript中的面向对象
初学JavaScript的时候有人会认为JavaScript不是一门面向对象的语言,因为JS是没有类的概念的,但是这并不代表JavaScript没有对象的存在,而且JavaScript也提供了其它的方 ...
- CSipSimple 简单分析
简介 CSipSimple是一款可以在android手机上使用的支持sip的网络电话软件,可以在上面设置使用callda网络电话.连接使用方式最好是使用wifi,或者3g这样上网速度快,打起电话来效果 ...
随机推荐
- 【前端】Vue.js经典开源项目汇总
Vue.js经典开源项目汇总 原文链接:http://www.cnblogs.com/huyong/p/6517949.html Vue是什么? Vue.js(读音 /vjuː/, 类似于 view) ...
- Scrapy之Scrapy shell
Scrapy Shell Scrapy终端是一个交互终端,我们可以在未启动spider的情况下尝试及调试代码,也可以用来测试XPath或CSS表达式,查看他们的工作方式,方便我们爬取的网页中提取的数据 ...
- centos vsftp 500 OOPS: cannot change directory:/home/ftp
系统是CentOS,是RH派系的.我把vsftpd安装配置好了,以为大功告成,但客户端访问提示如下错误:500 OOPS: cannot change directory:/home/ftp原因是他的 ...
- Codeforces Round #365 (Div. 2) B - Mishka and trip
http://codeforces.com/contest/703/problem/B 题意: 每个点都有一个值,每条边的权值为这两个点相乘.1~n成环.现在有k个城市,城市与其他所有点都相连,计算出 ...
- Log4j 2.0 使用说明(1) 之HelloWorld
以下是Log4j2.0的类图,以便大家对2.0有一个整体的理解. 就如我们学习任何一个技术一样,这里我们首先写一个Hello World: 1,新建工程TestLog4j 2,下载Log4j 2.0有 ...
- Spring Boot的自动配置的原理
Spring Boot在进行SpringApplication对象实例化时会加载META-INF/spring.factories文件,将该配置文件中的配置载入到Spring容器. 1.1.1. ...
- (转)浅谈SQL Server 对于内存的管理
简介 理解SQL Server对于内存的管理是对于SQL Server问题处理和性能调优的基本,本篇文章讲述SQL Server对于内存管理的内存原理. 二级存储(secondary storage) ...
- python UI自动化测试
为了减小维护成本: 1.UI自动化测试需要有较为稳定的环境 2.代码设计合理,那么我们就需要面向对象的设计一个框架,将重复的代码模块化 一.首先总结一下 UI自动化大概要哪些模块 1.config(配 ...
- 使用VMware出现的各种问题
ifconfig命令无效 解决办法:yum install net-tools ping不通 cd /etc/sysconfig/network-scripts ls查看所有文件名称,找到ifcfg- ...
- Zeratul的完美区间(线段树||RMQ模板题)
原题大意:原题链接 给定元素无重复数组,查询给定区间内元素是否连续 解体思路:由于无重复元素,所以如果区间内元素连续,则该区间内的最大值和最小值之差应该等于区间长度(r-l) 解法一:线段树(模板题) ...