一、okhttp工作的大致流程

1.1、整体流程

  1. 当我们通过OkhttpClient创建一个Call,并发起同步或异步请求时;
  2. okhttp会通过Dispatcher对我们所有的RealCall(Call的具体实现类)进行统一管理,并通过execute()及enqueue()方法对同步或异步请求进行处理;
  3. execute()及enqueue()这两个方法会最终调用RealCall中的getResponseWithInterceptorChain()方法,从拦截器链中获取返回结果;
  4. 拦截器链中,依次通过RetryAndFollowUpInterceptor(重定向拦截器)、BridgeInterceptor(桥接拦截器)、CacheInterceptor(缓存拦截器)、ConnectInterceptor(连接拦截器)、CallServerInterceptor(网络拦截器)对请求依次处理,与服务的建立连接后,获取返回数据,再经过上述拦截器依次处理后,最后将结果返回给调用方。

提供两张图便于理解和记忆:

okhttp整体流程1

okhttp整体流程2

这张图只画出了请求流程,没有数据返回流程,后期会处理。

1.2、各大拦截器的原理解析

  • 1.2.1、RetryAndFollowUpInterceptor:负责重定向

构建一个StreamAllocation对象,然后调用下一个拦截器获取结果,从返回结果中获取重定向的request,如果重定向的request不为空的话,并且不超过重定向最大次数的话就进行重定向,否则返回结果。

注意:这里是通过一个while(true)的循环完成下一轮的重定向请求。

(1)、StreamAllocation为什么在第一个拦截器中就进行创建?

       便于取消请求以及出错释放资源。

(2)、StreamAllocation的作用是什么?

       StreamAllocation负责统筹管理Connection、Stream、Call三个实体类,具体就是为一个Call(Realcall),寻找( findConnection() )一个Connection(RealConnection),获取一个Stream(HttpCode)。

  • 1.2.2、BridgeInterceptor

负责将原始Requset转换给发送给服务端的Request以及将Response转化成对调用方友好的Response。

具体就是对request添加Content-Type、Content-Length、cookie、Connection、Host、Accept-Encoding等请求头以及对返回结果进行解压、保持cookie等。

  • 1.2.3、CacheInterceptor

CacheInterceptor:负责读取缓存以及更新缓存。

在请求阶段:

  1. 读取候选缓存cacheCandidate;
  2. 根据originOequest和cacheresponse创建缓存策略CacheStrategy;
  3. 根据缓存策略,来决定是否使用网络或者使用缓存或者返回错误。
    具体的的缓存策略就是http的缓存策略,详见下图:
    在结果返回阶段:
    负责将网络结果进行缓存(使用于DiskLruCache)。

okhttp&http缓存策略

强制缓存:当客户端第一次请求数据时,服务端返回了缓存的过期时间(Expires与Cache-Control),没有过期就可以继续使用缓存,否则则不适用,无需再向服务端询问。

对比缓存:当客户端第一次请求数据时,服务端会将缓存标识(Etag/If-None-Match与Last-Modified/If-Modified-Since)与数据一起返回给客户端,客户端将两者都备份到缓存中 ,再次请求数据时,客户端将上次备份的缓存标识发送给服务端,服务端根据缓存标识进行判断,如果返回304,则表示缓存可用,如果返回200,标识缓存不可用,使用最新返回的数据。ETag是用资源标识码标识资源是否被修改,Last-Modified是用时间戳标识资源是否被修改。ETag优先级高于Last-Modified。

  • 1.2.4、ConnectInterceptor:负责与服务器建立连接

使用StreamAllocation.newStream来和服务端建立连接,并返回输入输出流(HttpCodec),实际上是通过StreamAllocation中的findConnection寻找一个可用的Connection,然后调用Connection的connect方法,使用socket与服务端建立连接。

  • 1.2.5、CallServerInterceptor:负责从服务器读取响应的数据

主要的工作就是把请求的Request写入到服务端,然后从服务端读取Response。

  1. 写入请求头
  2. 写入请求体
  3. 读取响应头
  4. 读取响应体

二、连接池原理

由于HTTP是基于TCP,TCP连接时需要经过三次握手,为了加快网络访问速度,我们可以Reuqst的header中将Connection设置为keepalive来复用连接。

Okhttp支持5个并发KeepAlive,默认链路生命为5分钟(链路空闲后,保持存活的时间),连接池有ConectionPool实现,对连接进行回收和管理。

2.1、连接池的清理

连接池清理1

ConectionPool在内部使用一个异步线程来清理连接。

当连接池中有连接时:清理任务由cleanup()方法完成,首先执行清理,并返回下次需要清理的间隔时间,调用调用wait() 方法释放锁。等时间到了以后,再次进行清理,并返回下一次需要清理的时间间隔,再次进入wait,以此循环往复。

当连接池中没有连接时:cleanup()返回-1,跳出循环,下次有连接加进来时,再次开启线程进行循环清理。

之所以连接池线程可以跳出循环,是因为,他是子线程,而looper选择一直阻塞是因为他是主线程,如果跳出,程序执行结束。

连接池原理2

首先统计空闲连接数量;

  1. 然后通过for循环查找最长空闲时间的连接以及对应空闲时长;
  2. 然后判断这个最长空闲时间的连接是否超出最大空闲连接数或者或者超过最大空闲时间,满足其一则清除最长空闲的连接。如果不满足清理条件,则返回一个对应等待时间。

这个对应等待的时间又分二种情况:

  1. 有空闲连接:则返回:keepAliveDurationNs-longestIdleDurationNs;
  2. 没有空闲的连接,则返回:keepAliveDurationNs

注意:清除一个空闲连接后,会返回0,再次立即开始清理。

如何统计空闲连接呢?

统计空闲连接

StreamAllocation创建或者复用一个Connection后,会将自己添加到Connection的connection.allocations列表中,数据读取完毕之后,会将自己从Connection的connection.allocations中移除,所以判读一个Connection是否是空闲连接可以采用引用计数法,判断connection.allocations列表中是否有StreamAllocation,如果没有就是空闲连接,否则不是。

三、OkHttp中Dispatcher和线程池

3.1、OkHttp中线程池

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;}

OkHttp中的线程池是一个 newCachedThreadPool。

所以在 OkHttp 中线程池只是一个辅助作用,仅仅是用来做线程缓存,便于复用的。

真正控制请求并发数量和执行时机是通过调度器 Dispatcher 完成的。

3.2、OkHttp中Dispatcher

RealCall.execute

RealCall.execute

先将RealCall加入Dispatcher的runningSyncCalls队列,然后调用getResponseWithInterceptorChain获取Response,最后调用Dispatcher的finished方法,将自身从runningSyncCalls移除,然后进行轮询readyAsyncCalls队列,取出ready的异步任务在满足条件的情况下进行执行。

RealCall.enqueue

RealCall.enqueue

如果当前正在执行的RealCall的数量小于最大并发数maxRequest(64),并且该call对应的Host上的call小于同一host上的最大并发数maxRequestsPerHos(5),则将该call加入runningAsyncCalls,并将这个call放到线程池中进行执行,否则加入readyAsyncCall排队等待。

  • 注意:

同步请求和异步请求执行完成之后,都会调用dispatcher的finished方法,将自身从对应的队列中移除,然后进行轮询readyAsyncCalls队列,取出ready的异步任务在满足条件下放到线程池中执行。

轮询readyAsyncCalls

  • Dispatcher.中的并发数量及三个队列的作用

maxRequests = 64 // 最大并发请求数为64

maxRequestsPerHost = 5 //每个主机最大请求数为5

ExecutorService executorService //消费者池(也就是线程池)

Deque<AsyncCall> readyAsyncCalls:// 异步的缓存,正在准备被消费的(用数组实现,可自动扩容,无大小限制)

Deque<AsyncCall> runningAsyncCalls //正在运行的 异步的任务集合,仅仅是用来引用正在运行的任务以判断并发量,注意它并不是消费者缓存

Deque<RealCall> runningSyncCalls //正在运行的,同步的任务集合。仅仅是用来引用正在运行的同步任务以判断并发量

四、OkHttp中的设计模式

责任链模式:拦截器链

单例模式:线程池

观察者模式:各种回调监听

策略模式:缓存策略

Builder模式:OkHttpClient的构建过程

外观模式:OkHttpClient封装了很多类对象

工厂模式:Socket的生产

五、OkHttp的优势

5.1、功能方面:

功能全面,满足了网络请求的大部分需求。

5.2、网络优化方面:

  1. 内置连接池,支持连接复用
  2. 支持gzip压缩响应体
  3. 通过缓存避免重复的请求
  4. 支持http2,对一台机器的所有请求共享同一个socket

5.3、扩展性方面:

拦截器模式使得我们很容易使得我们很容易添加一个自定义拦截器对请求和返回结果进行处理。

okhttp原理详解相关推荐

  1. Android中okhttp原理详解

    目录 1.okhttp工作的大致流程 2.okhttp中的连接 3.Dispatcher和线程池 4.OkHttp中的设计模式 5.OkHttp的优势 6.参考连接 1.okhttp工作的大致流程 1 ...

  2. android中okhttp原理详解,Android中okhttp原理详解-极度针对面试篇

    一.okhttp工做的大体流程 1.一.总体流程 (1).当咱们经过OkhttpClient创立一个Call,并发起同步或者异步请求时: (2).okhttp会经过Dispatcher对咱们全部的Re ...

  3. Android高工:okhttp原理详解,搞懂了直接去虐面试官

    当连接池中有连接时:清理任务由cleanup()方法完成,首先执行清理,并返回下次需要清理的间隔时间,调用调用wait() 方法释放锁.等时间到了以后,再次进行清理,并返回下一次需要清理的时间间隔,再 ...

  4. CRF(条件随机场)与Viterbi(维特比)算法原理详解

    摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA https://www.cnblogs.com/zhibei/p/9391014.html C ...

  5. LVS原理详解(3种工作方式8种调度算法)--老男孩

    一.LVS原理详解(4种工作方式8种调度算法) 集群简介 集群就是一组独立的计算机,协同工作,对外提供服务.对客户端来说像是一台服务器提供服务. LVS在企业架构中的位置: 以上的架构只是众多企业里面 ...

  6. jQuery中getJSON跨域原理详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp28 jQuery中getJSON跨域原理详解 前几天我再开发一个叫 河蟹工 ...

  7. nginx配置文件及工作原理详解

    nginx配置文件及工作原理详解 1 nginx配置文件的结构 2 nginx工作原理 1 nginx配置文件的结构 1)以下是nginx配置文件默认的主要内容: #user nobody; #配置用 ...

  8. EMD算法之Hilbert-Huang Transform原理详解和案例分析

    目录 Hilbert-Huang Transform 希尔伯特-黄变换 Section I 人物简介 Section II Hilbert-Huang的应用领域 Section III Hilbert ...

  9. 图像质量损失函数SSIM Loss的原理详解和代码具体实现

    本文转自微信公众号SIGAI 文章PDF见: http://www.tensorinfinity.com/paper_164.html http://www.360doc.com/content/19 ...

最新文章

  1. Bootstrap 表格
  2. Asp.Net中几种相似数据绑定标记符号的解释及用法
  3. FAT12中,如何定位大于一个扇区(512B)的文件内容
  4. ESFramework介绍之(14)-- AS与FS通信方案
  5. mysql mysqldumpslow_慢日志分析工具—mysqldumpslow 和 mysqlsla
  6. 二级java编写用户界面例题,单选题11—20:2012年计算机二级VB用户界面设计练习题及答案-计算机二级-233网校...
  7. Vs2012调试本地windows服务
  8. 靠一杯茶融资160亿
  9. 我在淘宝做前端的这三年 — 第二年
  10. hive、hadoop面试题
  11. 网站服务器历史解析记录查询,域名解析ip历史查询
  12. linux终端清除命令,ubuntu清除命令行记录
  13. 【托业】【新托业TOEIC新题型真题】学习笔记8-题库五-P7
  14. tp5模板使用php函数,tp5模板变量使用自定义函数
  15. [你好,2022] 月落乌啼霜满天,江枫渔火对愁眠
  16. 永磁同步电机的矢量控制策略(八)一一一仿真模型搭建与源代码
  17. 模块度(Modularity)与Fast Newman算法讲解与代码实现
  18. 【数据分析】系列-Python分析淘宝4200款Bra(没错,就是文胸)后,发现最好卖的款式居然是。。。
  19. 用青龙跑渤海宣传员(收益很稳定)
  20. vue 拖拽功能样式优化

热门文章

  1. 手机能打开的表白代码_悦生活|微信占满手机内存?教你一招,只要打开这个功能,就能释放大量内存...
  2. 小米扫地机器人划区清扫定时_终于可以放手让它自己去清洁卫生,小米米家扫地机器人体验 - IT168试客...
  3. 实况足球2011免费游戏
  4. X264编码的一些收藏
  5. Java哈希值HashCode理解
  6. 贝叶斯推理——概率思维
  7. css 文字平分,CSS 文字間隔
  8. CCE 部署 Mongodb
  9. 一个通过百度贴吧找到身份证失主的案例(供参考)
  10. DEF、LEF 文件