OkHttp 面试题汇总

2023-03-18  本文已影响0人  我要离开浪浪山

一、前言

如今面试中高级开发工程师岗位,OKhttp 原理是必问环节,只会使用已经无法满足 Android 开发市场的需求,优秀的第三方框架源码剖析不仅能深度理解框架,也能对自己学习带来很大的帮助。

本篇文章根据朋友反馈和亲身经历简单整理的一些关于 Okhttp 常见面试题目。

1.Okhttp 基本实现原理

OkHttp 主要是通过 5 个拦截器和 3 个双端队列(2 个异步队列,1 个同步队列)工作。内部实现通过一个责任链模式完成,将网络请求的各个阶段封装到各个链条中,实现了各层的解耦。

OkHttp 的底层是通过 Socket 发送 HTTP 请求与接受响应,但是 OkHttp 实现了连接池的概念,即对于同一主机的多个请求,可以公用一个 Socket 连接,而不是每次发送完 HTTP 请求就关闭底层的 Socket,这样就实现了连接池的概念。而 OkHttp 对 Socket 的读写操作使用的 OkIo 库进行了一层封装。

执行流程:

2.Okhttp 网络缓存如何实现?

OKHttp 默认只支持 get 请求的缓存。

3.Okhttp 网络连接怎么实现复用?

HttpEngine 在发起请求之前,会先调用nextConnection()来获取一个Connection对象,如果可以从ConnectionPool中获取一个Connection对象,就不会新建,如果无法获取,就会调用createnextConnection()来新建一个Connection对象,这就是 Okhttp 多路复用的核心,不像之前的网络框架,无论有没有,都会新建Connection对象。

20210312101552861.png

4.Dispatcher 的功能是什么?

Dispatcher中文是分发器的意思,和拦截器不同的是分发器不做事件处理,只做事件流向。他负责将每一次Requst进行分发,压栈到自己的线程池,并通过调用者自己不同的方式进行异步和同步处理。 通俗的讲就是主要维护任务队列的作用。

Dispatcher 类,该类中维护了三个双端队列(Deque):

readyAsyncCalls:准备运行的异步请求
runningAsyncCalls:正在运行的异步请求
runningSyncCalls:正在运行的同步请求

OkHttp 设置了默认的最大并发请求量 maxRequests = 64 和单个 Host 主机支持的最大并发量 maxRequestsPerHost = 5

5.addInterceptor 与 addNetworkInterceptor 的区别?

二者通常的叫法为应用拦截器和网络拦截器,从整个责任链路来看,应用拦截器是最先执行的拦截器,也就是用户自己设置request属性后的原始请求,而网络拦截器位于ConnectInterceptor和CallServerInterceptor之间,此时网络链路已经准备好,只等待发送请求数据。

1、首先,应用拦截器在RetryAndFollowUpInterceptor和CacheInterceptor之前,所以一旦发生错误重试或者网络重定向,网络拦截器可能执行多次,因为相当于进行了二次请求,但是应用拦截器永远只会触发一次。另外如果在CacheInterceptor中命中了缓存就不需要走网络请求了,因此会存在短路网络拦截器的情况。
2、其次,如上文提到除了CallServerInterceptor,每个拦截器都应该至少调用一次realChain.proceed方法。实际上在应用拦截器这层可以多次调用proceed方法(本地异常重试)或者不调用proceed方法(中断),但是网络拦截器这层连接已经准备好,可且仅可调用一次proceed方法。
3、最后,从使用场景看,应用拦截器因为只会调用一次,通常用于统计客户端的网络请求发起情况;而网络拦截器一次调用代表了一定会发起一次网络通信,因此通常可用于统计网络链路上传输的数据。

6、Okhttp 拦截器的作用是什么?

1、应用拦截器

拿到的是原始请求,可以添加一些自定义header、通用参数、参数加密、网关接入等等。

2、网络拦截器

用户自定义拦截器,通常用于监控网络层的数据传输。

7、Okhttp 有哪些优势?

8、response.body().string() 为什么只能调用一次?

我们可能习惯在获取到Response对象后,先response.body().string()打印一遍 Log,再进行数据解析,却发现第二次直接抛异常,其实直接跟源码进去看就发现,通过source拿到字节流以后,直接调用closeQuietly()方法关闭了,这样第二次再去通过source读取就直接流已关闭的异常了。

public final String string() throws IOException {
  BufferedSource source = source();
  try {
    Charset charset = Util.bomAwareCharset(source, charset());
    return source.readString(charset);
  } finally {
    //这里讲resource给悄悄close了
    Util.closeQuietly(source);
  }
}

解决方案:
1.内存缓存一份response.body().string();
2.自定义拦截器处理 Log。

9、OkHttp请求整体流程是怎么样?

var okHttpClient = OkHttpClient.Builder().build()
var request = Request.Builder().url("https://www.baidu.com")
       .cacheControl(CacheControl.FORCE_CACHE)
       .build()
var call = okHttpClient.newCall(request)
val result = call.execute()
println(result.isSuccessful)
result.close()
280da08ecf58408515783ab19d468ec.png

10、分发器是如何工作的?

  • 对于同步请求,分发器只记录请求,用于判断IdleRunnable是否需要执行;
  • 对于异步请求,向分发器中提交请求;

Q:如何决定将请求放入ready还是running?
A:如果当前正在请求数为64,则将请求放入ready等待执行;如果小于64,但是已经存在同一域名主机的请求5个,也会放入ready;否则放入running队列立即执行;

Q:从ready移动running的条件是什么?
A:每个请求执行完成就会从running移除,同时进行第一步相同逻辑的判断,决定是否移动!

11、拦截器是如何工作的?

12、应用拦截器与网络拦截器的区别?

OkHttp中拦截器有:自定义应用拦截器重试重定向桥接缓存连接自定义网络拦截器请求服务

自定义应用拦截器与自定义网络拦截器的区别主要是顺序的区别,由于以上拦截器采用责任链设计模式组合执行,因此顺序不同,带来的影响是:应用拦截器不需要关心是否重定向或者失败重连(只会执行一次);同时它也能决定是否执行其他拦截器而网络拦截器则可以操作重定向与重试并且可能不会执行(直接在缓存中获得结果),同时它也可以观察到真正的Request以及相关的连接信息(经过其他拦截器处理完毕后

13、OkHttp缓存机制

14、强缓存

  • 命中强缓存时,浏览器并不会将请求发送给服务器。强缓存是利用http的返回头中的Expires或者Cache- Control两个字段来控制的,用来表示资源的缓存时间;
  • 若未命中强缓存,则浏览器会将请求发送至服务器。服务器根据http头信息中的Last-Modify/If-Modify- Since或Etag/If-None-Match来判断是否命中协商缓存。如果命中,则http返回码为304,客户端从缓存中加载资源。
  1. expires,它的值为一个绝对时间,如果发送请求的时间在expires之前,那么本地缓存有效,能够直接使用缓存。

  2. cache-control:max-age=number,资源第一次的请求时间和该值相加,计算出一个资源过期时间,再拿这个过期时间跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存,否则就不行。另外此响应头还能设置为:

    • no-cache:不使用本地缓存。
    • no-store:不允许被缓存
    • public:可以被任何用户缓存,包括终端用户和CDN等中间代理服务器。
    • private:只能被终端用户缓存,不允许CDN等中间代理服务器缓存。
    • immutable:(响应)资源不会改变

15、协商缓存

协商缓存的意思是:浏览器会将请求发送至服务器。服务端可能响应304(不包含响应体数据)表示缓存可用,可能正常响应,如200同时携带响应体。为了让服务端判断是否可用缓存,在请求时,需要携带标识:**If-Modified-Since或者If-None-Match**

其中If-Modified-Since需要和响应的Last-Modified 配合使用,而If-None-Match 则与Etag 配合。

Last-Modified/ If-Modified-Since
在缓存的响应中响应头包含:Last-Modified ,表示服务端告知的对应请求的资源在服务器上的最后修改时间。再次发起请求,需要在请求头中携带:If-Modified-Since。其值就是响应中的Last-Modified的值。意思就是告诉服务端我的缓存,在服务端什么时候修改后拿到的。服务端判断这个时间后是否修改过资源,未修改则返回3-04,否则正常返回响应数据。

Etag/If-None-Match
这两个值是由服务器生成的每个资源的唯一标识字符串,只要资源有变化就这个值就会改变。在请求时携带If-None-Match,其值是缓存的响应头中的Etag,服务端获取到请求头中的If-None-Match会重新计算此次请求资源的标识,如果一致则返回304。

16、Okhttp 运用了哪些设计模式?

Okhttp 运用了六种设计模式:

17、HTTP1和HTTP2的区别

  • 1.新的二进制格式:HTTP2采用二进制格式而HTTP1使用文本格式。
  • 2.多路复用:HTTP2是完全多复用的,而非有序并阻塞的,只需一个连接即可实现并行。HTTP1一个连接只能发送一个请求。
  • 3.头部压缩:HTTP 1.1中,每一次发送和响应,都有HTTP头信息。HTTP 2压缩头信息,减少带宽。
  • 4.服务器推送:HTTP 1只能客户端发送数据,服务器端返回数据。HTTP2中,服务器可以主动向客户端发起一些数据传输(如css和png等),服务器可以并行发送html,css,js等数据。。

18、为什么需要头部压缩?

HTTP协议是不带有状态的,每次请求头部都会附上所有的信息,而且很多的信息都是重复的,这会浪费很多宽带也会影响速度,所以HTTP2对头部进行了压缩,一方面使用gzip或compress进行头部压缩,另一方面,客户端和服务器会同时维护同一张头信息表,所有的字段都会存入这张表中,生成一个索引号,以后就不需要再发送同样的字段了,只发送索引号,提示了速度。

上一篇下一篇

猜你喜欢

热点阅读