Okhttp相关知识点总结

OkHttp基本介绍

Google在Android4.4以后开始将源码中
的HttpURLConnection底层实现替换为OKHttp,同时现在流行的Retrofit框架底层同样是使用OKHttp的。

优点
  • 支持Http1、Http2、Quic以及WebSocket
  • 连接池复用底层TCP(Socket),减少请求延时
  • 缓存响应数据减少重复的网络请求
  • 请求失败自动重试主机的其他ip,自动重定向
使用流程

在这里插入图片描述

在使用OkHttp发起一次请求时,对于使用者最少存在 OkHttpClientRequestCall 三个角色。其中OkHttpClient 和 Request 的创建可以使用它为我们提供的 Builder (建造者模式)。而 Call 则是把Request交给OkHttpClient之后返回的一个已准备好执行的请求。

分发器Dispatcher

需要了解的成员变量:

//异步请求同时存在的最大请求 
private int maxRequests = 64; 
//异步请求同一域名同时存在的最大请求 
private int maxRequestsPerHost = 5; 
//异步请求使用的线程池 
private @Nullable ExecutorService executorService; 
//异步请求等待执行队列 
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); 
//异步请求正在执行队列 
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); 
//同步请求正在执行队列 
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
同步请求
synchronized void executed(RealCall call) {
 	runningSyncCalls.add(call); 
}

因为同步请求不需要线程池,也不存在任何限制。所以分发器仅做一下记录。

异步请求

当正在执行的任务未超过最大限制64,同时 runningCallsForHost(call) <maxRequestsPerHost 同一Host的请求不超过5个,则会添加到正在执行队列,同时提交给线程池。否则先加入等待队列。
加入线程池直接执行没啥好说的,但是如果加入等待队列后,就需要等待有空闲名额才开始执行。因此每次执行完一个请求后,都会调用分发器的 finished 方法

分发器的线程池
  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
  • 核心线程数0表示不会缓存线程,线程池中的所有线程60s没工作后就收回
  • 最大线程数int的最大值和SynchronousQueue可以实现最大吞吐量,下面细讲!
  • 空闲线程闲置时间 60s
  • 线程等待队列是一个无容量队列
  • 线程创建工厂
    阻塞队列一般有3种
  • ArrayBlockingQueue
  • LinkedBlockingQueue
  • SynchronousQueue

如果使用ArrayBlockingQueue需要填入size,假设为1,那么我第一个任务塞进去执行了(addWorker方法),然后来了第二个任务,因为核心线程数为0,所以只能往等待队列里添加,而刚才等待队列里面的第一个任务已经创建线程去运行了,所以第二个任务可以添加成功!此时任务一还没有完成,于是在等待队列里面等待,此时又来了第三个任务,发现阻塞队列里已经有1个了,队列满了,于是加入失败,再判断没有大于最大线程数,于是直接起新的线程来执行第三个任务了,此时出现1----3-----2不再是顺序执行!!且任务3和任务2都是同一个线程处理的,因为线程3处理完之后,有假设30s的存活时间,那么开始从等待队列里取任务来执行!当然如果size填入其他数字也依旧还是会出现这样的问题。

LinkedBlockingQueue如果填入size效果和上线一样。
SynchronousQueue使用此队列意味着希望获得最大并发量。因为无论如何,向线程池提交任务,往队列提交任务都会失败。而失败后如果没有空闲的非核心线程,就会检查如果当前线程池中的线程数未达到最大线程,则会新建线程执行新提交的任务。完全没有任何等待,唯一制约它的就是最大线程数的个数。因此一般配合 Integer.MAX_VALUE 就实现了真正的无等待。

责任链拦截器

重试及重定向拦截器

本拦截器是整个责任链中的第一个,这意味着它会是首次接触到 Request 与最后接收到 Response 的角色,在这个拦截器中主要功能就是判断是否需要重试与重定向。
重试的前提是出现了 RouteException 或者 IOException 。一但在后续的拦截器执行过程中出现这两个异常,就会通过 recover 方法进行判断是否进行连接重试。
重定向发生在重试的判定之后,如果不满足重试的条件,还需要进一步调用 followUpRequest 根据Response 的响应码(当然,如果直接请求失败, Response 都不存在就会抛出异常)。
followup 最大发生20次。

桥接拦截器

连接应用程序和服务器的桥梁,我们发出的请求将会经过它的处理才能发给服务器,比如设置请求内容长度,编码,gzip压缩,cookie等,获取响应后保存Cookie等操作。

缓存拦截器

在发出请求前,判断是否命中缓存。如果命中则可以不请求,直接使用缓存的响应。 (只会存Get请求的缓存。。。。)。

1、如果从缓存获取的 Response 是null,那就需要使用网络请求获取响应;
2、如果是Https请求,但是又丢失了握手信息,那也不能使用缓存,需要进行网络请求;
3、如果判断响应码不能缓存且响应头有 no-store 标识,那就需要进行网络请求;
4、如果请求头有 no-cache 标识或者有 If-Modified-Since/If-None-Match ,那么需要进行网络请求;
5、如果响应头没有 no-cache 标识,且缓存时间没有超过极限时间,那么可以使用缓存,不需要进行网络请求;
6、如果缓存过期了,判断响应头是否设置 Etag/Last-Modified/Date ,没有那就直接使用网络请求否则需要考虑服务器返回304;并且,只要需要进行网络请求,请求头中就不能包含 only-if-cached ,否则框架直接返回504!

连接拦截器

这个拦截器中的所有实现都是为了获得一份与目标服务器的连接,在这个连接上进行HTTP数据的收发。

请求服务器拦截器

真正发起HTTP请求,并拿回响应报文。

总结

整个OkHttp功能的实现就在这五个默认的拦截器中,所以先理解拦截器模式的工作机制是先决条件。
这五个拦截器分别为: 重试拦截器桥接拦截器缓存拦截器连接拦截器请求服务拦截器。每一个拦截器负责的工作不一样,就好像工厂流水线,最终经过这五道工序,就成了最终的产品。
但是与流水线不同的是,OkHttp中的拦截器每次发起请求都会在交给下一个拦截器之前干一些事情,在获得了结果之后又干一些事情。整个过程在请求向是顺序的,而响应则是逆序。
当用户发起一个请求后,会由任务分发起 Dispatcher 将请求包装并交给重试拦截器处理。

  1. 重试拦截器在交出(交给下一个拦截器)之前,负责判断用户是否取消了请求;在获得了结果之后,会根据响应码判断是否需要重定向,如果满足条件那么就会重启执行所有拦截器。
  2. 桥接拦截器在交出之前,负责将HTTP协议必备的请求头加入其中(如:Host)并添加一些默认的行为(如:GZIP压缩);在获得了结果后,调用保存cookie接口并解析GZIP数据。
  3. 缓存拦截器顾名思义,交出之前读取并判断是否使用缓存;获得结果后判断是否缓存。
  4. 连接拦截器在交出之前,负责找到或者新建一个连接,并获得对应的socket流;在获得结果后不进行额外的处 理。
  5. 请求服务器拦截器进行真正的与服务器的通信,向服务器发送数据,解析读取的响应数据。在经过了这一系列的流程后,就完成了一次HTTP请求!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alex_ChuTT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值