Android 深入Http(4)从OkHttp源码来看Http,Android开发面试问题

.url(url)

.build())

.enqueue(new Callback() {

@Override

public void onFailure(Call call, IOException e) {

Log.d(TAG,“onFailure”);

}

@Override

public void onResponse(Call call, Response response) throws IOException {

Log.d(TAG,“onResponse”);

}

});

嗨呀,用法顶多就这样,一般情况下我们都不会直接使用OkHttp,更多是用Retrofit。(Retrofit的低层是Okhttp,所以Retrofit能做的,OkHttp都能做)

所以我们就来分析OkHttp的源码,来分析它其中的Http是怎么工作的。

OkHttp源码分析

==========================================================================

newCall


分析源码从执行入口看起,在这里就是enqueue(),发现它是从newCall()调用的。所以最开始,我们来分析newCall的代码,

//OkHttp使用结构

client.newCall(new Request.Builder()…).enqueue(…);

我们发现newCall传入的是一个Request,没错,我们后面用Builder模式拼的这一串玩意,其实就是Http的Request。我们点进newCall的源码:

@Override public Call newCall(Request request) {

return new RealCall(this, request, false /* for web socket */);

}

↑括号中的参数就是一个正经的Requeset对象。

再来看返回,函数的返回是 Call类型,而下面实际return的是一个 new RealCall(.. request ..) 对象

也就是说,RealCallCall的一个实现

我们点进RealCall:

RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket){

this.eventListener = eventListenerFactory.create(this);

}

分析下构造参数

  • OkHttpClient client

大总管,先不管

  • Request originalRequest

这里的Request是我们通过Build()创建出来的,这里为什么叫originalRequest呢?

是因为后面我们会不断的对Request做操作(就是用拦截器啊!)把它这改一下那里改一下

  • boolean forWebSocket

来判断是不是WebSocket,WebSocket就是Http的一种扩展(可以做到服务器对客户端做推送),一般都不是WebSocket

然后我们来看下下面的eventListener,在http的一个完整的过程中,会有一些关键的时间点,比如 Http的响应,tcp层的建立等等,而在这些时间点产生的事件,则会通过 eventListener去响应。

到这里,newCall就看完了,它就是产生一个 RealCall的对象。

enqueue()


接下来我们来看okhttp的入口 enqueue(Callback),因为它实际是由RealCall执行的,所以我们要去RealCall类中找enqueue()方法:

@Override public void enqueue(Callback responseCallback) {

synchronized (this) {

//并列执行

if (executed) throw new IllegalStateException(“Already Executed”);

executed = true;

}

captureCallStackTrace();

//这行信息量很大,也很重要

client.dispatcher().enqueue(new AsyncCall(responseCallback));

}

我们来看最后一行,看到dispatcher就知道是分发器,它拿去分发Callback了,并且分发之前,将它包装成一个 AsyncCall对象。

我们先来啃Dispatcher

Dispather

它是一个线程管理工具。

因为Http的Request和Response要使用单独的线程,如果你有多个请求和返回,如果都放在同一个线程的话会被挡到,所以Dispatch来做到线程控制

在这里插入图片描述

看到Executor懂得人都懂了,它是专门处理线程的类。但这里不讲解了,它就是用来处理线程的。

还看到有 maxRequests和maxRequestsPerHost

  • maxRequests

最多并列执行又这么多(64)的请求,后面多出来的请求就放在后面的队列等。

  • maxRequestsPerHost

每个主机最多并列执行又这么多(5)的请求。多出来的往后稍稍。

Dispatcer的enqueue()方法

synchronized void enqueue(AsyncCall call) {

if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {

runningAsyncCalls.add(call);

//如果当前并发执行的网络请求少于上限,则将该网络请求放到后台中执行

executorService().execute(call);

} else {

//否则放到ready队列等待

readyAsyncCalls.add(call);

}

}

这个方法比较简单,就是判断当前并行的网络请求有没有达到上限,有就等,没有就放到后台执行。

然后我们来看它具体执行的AsyncCall对象。

AsyncCall

在这里插入图片描述

发现作为一个Call对象它是没有run方法的,所以我们要去它的父类 NamedRunnable对象里面看。

在这里插入图片描述

发现NamedRunnable实现了Runnable方法并重写了run()方法,run方法里就是跑了 execute()方法。

此execute()非彼execute(),点进它,它是 AsyncCall继承该类重写的execute()方法。

这个execute()方法在 AsyncCall下,

在这里插入图片描述

想必最重要的是哪几行,大家都知道了。

Response response = getResponseWithInterceptorChain()

这行代码就突然Response了,这说明getResponseWithInterceptorChain()把Http的请求响应给走完了。也就是说它是最最最最关键的方法了。

已经了解完大框架,小结一下


学到这里我们看了好几个类,它们大概做的就是为Http铺好一条路,让http请求更好走,比如给它安排了线程,给Request包装等等,我们看一下关键的步骤:

  1. 将请求Requeset通过newCall方法包装成一个RealCall

  2. 执行RealCall()enqueue()方法,并且将Callback回调作为参数代入

  3. Dispatcher给该Request分配线程,放在后台,执行Dispatch自己的 execute()方法

  4. 这个execute()方法内部执行了 getResponseWithInterceptorChain(),该方法内部走完了http的传输细节,返回Response

  5. Response响应外面CallBack的 onResponse()onFailure()

注:其实这里就可以看出OkHttp无论是enqueue()还是execute(),都会走getResponseWithInterceptorChain(),因为execute()它不用切线程,所以它是直接执行该方法,而enqueue还要考虑当前请求是否太多,否则会造成阻塞,所以要用Dispatch来管理一下,再使用这个方法。

然后走完这个方法,就会去回调之前Callback的onResponse和onFailure方法了。

上面就是OkHttp的大框架了,其实很简单,所以外面用起来也很简单,好懂。

为了更深入的了解OkHttp,我们除了了解这些,还要去了解它里面的一些配置,和网络实现的细节。

也就是OkHttpClient 它是做什么的,和 getResponseWithInterceptorChain()这个方法

OkHttpClient


我们先来看看OkHttpClient里面的全部配置。每个都注释一下,并讲述比较重要的对象:

//线程管理器,这个之前已经了解了

final Dispatcher dispatcher;

//代理类,帮我们配置网络信息

final @Nullable Proxy proxy;

//Protocol类里面列出了支持的Http版本,http1.0、1.1、2.0 SPDY3.1(http2的前身)

final List protocols;

//里面是Cipher Suite(如果是Https,那么这个Cipher suite就是TLS版本、对称加密、非对称加密、Hash算法…,让对方选,所以这里是一个列表)

//它有好几套Cipher Suite方案任君选择

final List connectionSpecs;

//这两个比较重要,等下会具体研究,它们大概就是对连接过程中的每一个阶段做处理

final List interceptors;

final List networkInterceptors;

//就是监听整个网络请求+返回过程

final EventListener.Factory eventListenerFactory;

//用处不大,不用了解了

final ProxySelector proxySelector;

//Jar是罐子的意思,CookieJar是存放Cookie的容器

final CookieJar cookieJar;

//Http的Cache

final @Nullable Cache cache;

final @Nullable InternalCache internalCache;

//Socket是Tcp的端口,这个就是Tcp端口的工厂

final SocketFactory socketFactory;

//Https下Tcp的端口

final @Nullable SSLSocketFactory sslSocketFactory;

//证书链,方便验证(https讲过)

final @Nullable CertificateChainCleaner certificateChainCleaner;

//Https验证的 主机名验证器, 验证对方的Host是不是想要访问的Host

final HostnameVerifier hostnameVerifier;

//直译叫证书固定器,用来做自签名,用来验证证书公钥,如果host的证书和这个对象传入(本地存的)的公钥一致,则证明对方是自己想要访问的一方。

final CertificatePinner certificatePinner;

//用来写 Authorization Bearer<…> 当权限不足的时候会报错 401

final Authenticator proxyAuthenticator;

final Authenticator authenticator;

//线程连接池,可以有那么多的线程可以用来进行网络请求

final ConnectionPool connectionPool;

//DNS实现类

final Dns dns;

//重定向跳转, 默认为true,即跳转

final boolean followRedirects;

//https跳到http重定向,默认为true

final boolean followSslRedirects;

//当连接建立失败后,是否重试,默认为true

final boolean retryOnConnectionFailure;

//Tcp连接、读、写 超过该时间就跑错

final int connectTimeout;

final int readTimeout;

final int writeTimeout;

//长连接,发送ping(相当于一个心跳)的时间间隔

final int pingInterval;

看完上面的配置还有一些注释,会很明白HTTP在OkHttp里面的配置,比较全而且清晰。

OkHttpClient这个大总管它有关于Http、Https基本都有的配置信息,基本都是Header和Header有关的一些东西。

getResponseWithInterceptorChain()


OkHttp最核心的方法,相当于内部的入口。所以很有必要去了解。

我们先点进这个方法:

Response getResponseWithInterceptorChain() throws IOException {

// Build a full stack of interceptors.

//第一部分

List interceptors = new ArrayList<>();

interceptors.addAll(client.interceptors());

interceptors.add(retryAndFollowUpInterceptor);

interceptors.add(new BridgeInterceptor(client.cookieJar()));

interceptors.add(new CacheInterceptor(client.internalCache()));

interceptors.add(new ConnectInterceptor(client));

if (!forWebSocket) {

interceptors.addAll(client.networkInterceptors());

}

interceptors.add(new CallServerInterceptor(forWebSocket));

//第二部分

Interceptor.Chain chain = new RealInterceptorChain(

interceptors, null, null, null, 0, originalRequest);

//第三部分

return chain.proceed(originalRequest);

}

上面比较“麻雀虽小五脏俱全”的感觉,代码没那么多,但每一行都是核心。

它大概有三个部分:

  1. 装一些Interceptor到List中

  2. 构建一个 Interceptor Chain

  3. 使用chain的 proceed(originalRequest)得到了网络响应的Response

我们用图分别来说下他们的概念:

在这里插入图片描述

首先这个方法会创建一个含有泛型为Interceptor的List,并往这个List里面add几个系统自带的Interceptor。如上图所示。

chain的构造方法中带了这个List,也就是说,这个chain它其实是给这个List里的所有interceptor用一条链,按顺序给链了起来,如下图:

在这里插入图片描述

↑而且实际上,这条链并不是单向的,它走到走后的Interceptor后,会往回走。

原因很简单,chain的方向就是我们Request走的方向,我们把Request带过去(发送请求),最后会把Respnse带回来,回来的时候,也要这些Interceptor来处理结果。这就是chain。

为什么要用Chain和Interceptor,为什么不直接发到另一端去


那肯定是细节化啊。

万一你突然Request少了一个重要东西,我有必要在检查少了东西的这个环节进行上报,如果直接传过去,可能会产生不必要的开销。

chain的目的在于每个Interceptor都能够自定义的去处理这些事情,所以chain和Interceptor是多么滴重要。

chain.proceed(Requset)


process(request)是做什么的呢?process是前进的意思。

概述:将Request交个下一个Interceptor处理

将代码和图来讲一下:

它有三个情况,一个是将requeset,传过去,一个是等,一个是request/response回来:

(1)执行proceed()方法

在这里插入图片描述

Interceptor1的时候执行了proceed(request),将request传给Interceptor2

(2)等Chain回来

在这里插入图片描述

(3)Chain回来了,得到Interceptor2给Interceptor的东西了,就可以去处理这个东西了

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

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

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

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

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

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

学习分享

在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了

很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021最新上万页的大厂面试真题

七大模块学习资料:如NDK模块开发、Android框架体系架构…

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

74294)]

七大模块学习资料:如NDK模块开发、Android框架体系架构…

[外链图片转存中…(img-aYbd7rz6-1712148274295)]

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值