OkHttp 系列 - 开篇

214 阅读2分钟

前言

最近在看 OkHttp 原理,发现网上很多都是对源码的行级别的分析,感觉再写一篇意义不大,主要还是看背后的设计思想和设计模式,记录一下自己的所看所想,所以本篇不会有大段的源码分析和细节,更多的是在更高的层面进行总结。

写这篇文章一是好记性不如烂笔头,二是文笔太菜想练练文笔,大家轻喷哈~

看完本文,你会对以下知识点有所了解:

  • 网络模型
    • HTTP 协议
    • TCP 长连接
    • 多路复用
  • 设计模式
    • 建造者模式
    • 责任链模式

网络协议

OSI 网络模型

说到网络协议,那绕不开 OSI 七层模型:

重点看应用层、传输层和网络层,我们要发送的请求和数据会先通过 HTTP 协议进行一次封装,然后经过 TCP 协议三次握手与服务器建立连接,然后通过 IP 找到需要发送的地址。

HTTP 历代版本

HTTP 历代版本的区别:

  • HTTP/1.0 小菜鸟,一次 TCP 连接对应一次 HTTP 通信。耗时、低效。

  • HTTP/1.1 稍有改观,使用 Connection:Keep-Alive 实现 TCP 长连接,不过 HTTP 通信是串行的,还是耗时。

  • HTTP/2.0 老大哥,实现了 HTTP 并行通信,基于流和帧封装实现,简单理解就是把数据切块,以块为单位发送,快速、强大。

TCP + OkHttp 实现多路复用

运用 Keep-Alive 可以实现 TCP 长连接,可是光有 HTTP 这边的 Keep-Alive 是不够的,还需要客户端配合它来实现,OkHttp 的ConnectionPool 类实现了 TCP 连接的复用。

简单的看下实现:

public final class RealConnectionPool {

  private static final Executor executor = new ThreadPoolExecutor(...);

  private final Runnable cleanupRunnable = () -> {
      cleanup(System.nanoTime());
    }
  };

  private final Deque<RealConnection> connections = new ArrayDeque<>();
}

删去多余代码后,可以发现使用了一个队列存放连接,使用了一个线程来清除废弃的连接。至于清除废弃连接的原理等,这里不展开了。

OkHttp

RealCall 的构造器值得看一下:

final class RealCall implements Call {

  private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
  }

  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.transmitter = new Transmitter(client, call);
    return call;
  }
    
}

从静态方法 newRealCall 中可以看到,实例化 RealCall 需要两个步骤,所以用静态方法封装了,这个技巧我们在业务中也可以使用起来。

异步请求流程

一个异步网络请求从出生到获取服务器响应的流程:

  1. Activity 通过 OkHttpClient 获得一个 Call
  2. Activity 使用 Call.enqueue 回调方法开始一个异步网络请求
  3. Call 内部会生成一个 AsyncCall,由 AsyncCall 负责异步调用网络请求和结果的回调
  4. DispatcherasyncCall 放入线程池 DiapatcherExecutorService
  5. 线程池调用 asyncCall#run
  6. asyncCall 通过 InterceptorChain 的几个拦截器的处理,获得最终的 response,最后通过回调把 responseActivity

从另一个角度看流程:

简要概括异步网络请求的全流程:OkHttpClient 根据我们提供的 request 生成一个 call,并放入 Dispatcher 中,这个 call 经由 Dispatcher 的线程池捞出来执行,通过一个个拦截器的处理后,到达服务器,服务器返回的 response 再经过这几个拦截器的处理,得到了最终的结果。拦截器这块要细细分析的话又是一篇文章了。

设计模式

OkHttp 用到的一些设计模式:

  • 因为 OkHttpClient 的实例化需要很多的参数,大部分参数是有默认实现的,所以使用了建造者模式。同理,RequestResponse 的初始化也用了建造者模式

  • http 的处理过程有多个步骤,比如 headers 的处理、重定向、缓存、长连接等,如果把这些功能都写在一个类里,那这个类会很臃肿,可读性可扩展性差,所以这些步骤被多个类分别实现,每个类各司其职,同时,处理好的数据需要流转给下一个步骤,所以很自然的想到使用责任链模式实现,遂形成了 OkHttp 的核心功能:拦截器

  • 工厂模式生成 RealCallOkHttpClient 是个工厂,使用者只需要调用 OkHttpClient 就行,无需关心 new RealCall 的细节。

总结

本篇介绍了 HTTP 协议和 TCP 协议的长连接部分,然后引出 OkHttp 中对长连接的实现。简单过了一下使用 OkHttp 进行异步请求的流程。介绍了 OkHttp 用到的设计模式。除了这些,还有很多的地方没有覆盖到,比如拦截器的实现,连接池原理、缓存等,以后再填坑。

Resources

深入理解OKHttp源码

手写 okhttp

手写 okhttp 简易版

像白话文一样,深入理解OkHttp源码

okhttp源码学习笔记(一)-- 综述

www.jianshu.com/p/6166d2898…

OKHttp源码解析(二):"前戏"——HTTP的那些事

TCP/IP、Http、Socket的区别

OkHttp 源码剖析系列(六)——连接复用机制及连接的建立

拦截器机制及大致流程分析