深入解析OkHttp的实现原理

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

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

执行流程:

通过构建者构建出OkHttpClient对象,再通过newCall方法获得RealCall请求对象.

通过RealCall发起同步或异步请求,而决定是异步还是同步请求的是由线程分发器dispatcher来决定.

当发起同步请求时会将请求加入到同步队列中依次执行,所以会阻塞UI线程,需要开启子线程执行.

当发起异步请求时会创建一个线程池,并且判断请求队列是否大于最大请求队列64,请求主机数是否大于5,如果大于请求添加到异步等待队列中,否则添加到异步执行队列,并执行任务.
 

Okhttp 网络缓存如何实现?

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

  • 第一次拿到响应后根据头信息决定是否缓存。
  • 下次请求时判断是否存在本地缓存,是否需要使用对比缓存、封装请求头信息等等。
  • 如果缓存失效或者需要对比缓存则发出网络请求,否则使用本地缓存。
  • Okhttp 网络连接怎么实现复用?
    HttpEngine 在发起请求之前,会先调用nextConnection()来获取一个Connection对象,如果可以从ConnectionPool中获取一个Connection对象,就不会新建,如果无法获取,就会调用createnextConnection()来新建一个Connection对象,这就是 Okhttp 多路复用的核心,不像之前的网络框架,无论有没有,都会新建Connection对象。

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

    记录同步任务、异步任务及等待执行的异步任务。
    调度线程池管理异步任务。
    发起/取消网络请求 API:execute、enqueue、cancel。
    Dispatcher 类,该类中维护了三个双端队列(Deque):

    readyAsyncCalls:准备运行的异步请求
    runningAsyncCalls:正在运行的异步请求
    runningSyncCalls:正在运行的同步请求
    OkHttp 设置了默认的最大并发请求量 maxRequests = 64 和单个 Host 主机支持的最大并发量 maxRequestsPerHost = 5

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

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

    Okhttp 拦截器的作用是什么?
    1、应用拦截器
    拿到的是原始请求,可以添加一些自定义header、通用参数、参数加密、网关接入等等。

    RetryAndFollowUpInterceptor 处理错误重试和重定向

    BridgeInterceptor 应用层和网络层的桥接拦截器,主要工作是为请求添加cookie、添加固定的header,比如Host、Content-Length、Content-Type、User-Agent等等,然后保存响应结果的cookie,如果响应使用gzip压缩过,则还需要进行解压。

    CacheInterceptor 缓存拦截器,如果命中缓存则不会发起网络请求。

    ConnectInterceptor 连接拦截器,内部会维护一个连接池,负责连接复用、创建连接(三次握手等等)、释放连接以及创建连接上的socket流。

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

    CallServerInterceptor 请求拦截器,在前置准备工作完成后,真正发起了网络请求。

    Okhttp 有哪些优势?

    1. 支持 http2,对一台机器的所有请求共享同一个 Socket
    2. 内置连接池,支持连接复用,减少延迟
    3. 支持透明的 gzip 压缩响应体
    4. 响应缓存可以完全避免网络重复请求
    5. 请求失败时自动重试主机的其他 ip,自动重定向
    6. 丰富的 API,可扩展性好

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

    Okhttp 运用了哪些设计模式?
    Okhttp 运用了六种设计模式:

    构造者模式(OkhttpClient,Request 等各种对象的创建)
    工厂模式(在 Call 接口中,有一个内部工厂 Factory 接口。)
    单例模式(Platform 类,已经使用 Okhttp 时使用单例)
    策略模式(在 CacheInterceptor 中,在响应数据的选择中使用了策略模式,选择缓存数据还是选择网络访问。)
    责任链模式(拦截器的链式调用)
    享元模式(Dispatcher 的线程池中,不限量的线程池实现了对象复用)
     

    物联沃分享整理
    物联沃-IOTWORD物联网 » 深入解析OkHttp的实现原理

    发表评论