手把手讲解OKhttp硬核知识点(2)下(1)

public boolean isEligible(Address address, @Nullable Route route) {
// If this connection is not accepting new streams, we’re done.
if (allocations.size() >= allocationLimit || noNewStreams) return false;

// If the non-host fields of the address don’t overlap, we’re done.
if (!Internal.instance.equalsNonHost(this.route.address(), address)) return false;

// If the host exactly matches, we’re done: this connection can carry the address.
if (address.url().host().equals(this.route().address().url().host())) {
return true; // This connection is a perfect match.
}

// At this point we don’t have a hostname match. But we still be able to carry the request if
// our connection coalescing requirements are met. See also:
// https://hpbn.co/optimizing-application-delivery/#eliminate-domain-sharding
// https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing/

// 1. This connection must be HTTP/2.
if (http2Connection == null) return false;

// 2. The routes must share an IP address. This requires us to have a DNS address for both
// hosts, which only happens after route planning. We can’t coalesce connections that use a
// proxy, since proxies don’t tell us the origin server’s IP address.
if (route == null) return false;
if (route.proxy().type() != Proxy.Type.DIRECT) return false;
if (this.route.proxy().type() != Proxy.Type.DIRECT) return false;
if (!this.route.socketAddress().equals(route.socketAddress())) return false;

// 3. This connection’s server certificate’s must cover the new host.
if (route.address().hostnameVerifier() != OkHostnameVerifier.INSTANCE) return false;
if (!supportsUrl(address.url())) return false;

// 4. Certificate pinning must match the host.
try {
address.certificatePinner().check(address.url().host(), handshake().peerCertificates());
} catch (SSLPeerUnverifiedException e) {
return false;
}

return true; // The caller’s address can be carried by this connection.
}

解读一下这段代码:

  • 返回值: 如果这个连接可以给这个访问地址一个流,那么返回true。(可见,判断连接是否可用,会考虑当前访问的ip地址是否匹配,很好理解,如果访问的ip都不一样,我凭什么把现成的连接给你复用。)

然后是众多if判断(这里存在一个当前连接目标地址的参数值对比),总结起来:

  • 连接到达最大并发流或者连接不允许建立新的流,那就不允许复用;
  • 如果地址的非host字段没有完全相同,也不允许复用。(解释一下,一个Address对象包含了host字段和其他字段,这里的非host字段,就是指的这个类中的其他字段)
  • 如果到了这里,那就判断host是不是相同,如果相同,那就允许复用。
  • 如果上面的3步判断都没有命中,那么,我们依然有机会去复用,只要不命中下面的判断。
    1)HTTP2连接为空,不允许复用
    2)使用了代理,并且代理的类型不是直接代理 或者 ,不允许复用
    3)此连接的服务器证书必须覆盖新主机,否则也不能复用
    4)证书固定必须与主机匹配,否则,也不能复用
    上面的4个if都没有命中,那么还是判定为可以复用。

总结:如果在连接池中找到个连接参数一致并且未被关闭没被占用的连接,则可以复用旧连接,无需新建连接


5. 服务调用拦截器 CallServerInterceptor

经历了 上面4个拦截器,我们最终拿到了 requestconnection,有了请求,有了连接,那么就可以向服务器发起网络请求了。这一步,就包含执行网络请求的具体逻辑。

注意:我们这里的request,它是一个java对象,但是底层用的TCP协议,发送的是报文(在http1.X下,报文全都是明文字符串格式拼接,那么你一个request对象中包含的内容如何去拼接成一个 报文字符串呢?)
反过来想,我们TCP底层从服务器取得的也最先是 响应报文,他也是一大串字符串,那么如何封装成java需要的Response对象呢?逻辑也在这里。具体在哪个类?

HttpCodeC.java
在这里我关心两个问题:
1)真正与底层发生socket网络通信是如何进行的?
2)httpCodeC是如何解析request成请求报文,又是如何将相应报文变成Response的?

来探索,首先进入到CallServerInterceptorInterceptor方法,这是核心入口:

image.png

首先回顾一下TCP socket通信的过程:
1)建立Socket连接
2)打开IO流通道(socket是双工通讯协议,可以同时调用输入和输出流)
3)在这个案例中,移动端作为客户端需要往IO流通道中去写数据,然而我们知道,OutputStream,当我们写了数据之后,必须调用flush,刷新缓存区,才能将数据发送过去。
这里,我们就能得出上面一个问题的答案:

Q:真正与底层发生socket网络通信是如何进行的?
A:OkHttp使用了 OkIo来发送数据给远端,在这个Intercept 方法中,我们可以找到两句代码:httpCodec.flushRequest();httpCodec.finishRequest(); 如果你进去看实现,会发现,他们执行的是同一个flush过程,将缓存去数据发送给远端。(至于更细节的,比如 sink.flush是如何实现的,我就不去探索了,有兴趣的可以继续深入)

下面来探究问题2:
Q: httpCodeC是如何解析request成请求报文,又是如何将相应报文变成Response的?
继续在Intercept方法中寻找,发现:

httpCodec.writeRequestHeaders(request); //写入请求头
httpCodec.createRequestBody(request, contentLength) //构建请求体

深入可以发现,这两个方法其实都是接口方法,他们的具体实现分为了http1 和http2 ,两者实现并不相同,我们只看http1的:

image.png
image.png
看到了吧,这里其实就是在利用 io通道 sink来写入字符串而已。至此,request变成字符串报文已经有答案了。

那么,报文如何变成response对象呢?
响应,分为响应头和响应体,先看响应头:

image.png
image.png
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

尾声

如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

这里,笔者分享一份从架构哲学的层面来剖析的视频及资料给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。

Android进阶学习资料库

一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!

大厂面试真题

PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

《2019-2021字节跳动Android面试历年真题解析》

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

解析》**

[外链图片转存中…(img-gZYIOHED-1712416826996)]

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值