OkHttp(基于源码的核心理解)

作为android著名开源厂商square的主打产品,OkHttp可谓是具有诸多优点,毕竟站在巨人的肩膀上,吸取了众多网络框架的优点,提升了稳定性。
对于不会使用OkHttp的,推荐泡在网上的日子的一片博文OkHttp使用教程
OkHttp的调用可以简单的分为以下几步

  1. 生成一个OkHttpClient(用以总的控制)
  2. 用各种键值对包装我们的Request
  3. 将请求加入队列生成一个Call管理请求
  4. 若是同步请求直接执行excute等待处理返回Response,若为异步则实现Callback回调,在onResponse里获取Response参数

因此我们的分析也将从这几个步骤出发。
首先对于OkHttpClient来说,OkHttp官方文档并不建议我们创建多个OkHttpClient,因此全局使用一个。 如果有需要,可以使用clone方法,再进行自定义,类似于数据操作类,采用单例模式有利于我们省却同步问题,以及节省性能。
当我们初始化OkHttpClient的时候,其内部先生成了一个Builder,然后初始化一系列管理器,以及初始化参数

OkHttpClient的Builder初始化参数

这里我们着重分析下Dispatcher,首先我们需要了解下线程池,以及反向代理模式

线程池技术

相比我们对于异步任务的需求应该遇到了不少,首先想到的便是Thread,handler等异步机制,Java已经做了很好的封装,但是当我们需要使用许多异步任务,而这些任务只做了一点点事情就完成了它的使命,当我们不断的创建,销毁线程的时候,对系统的开销是相当大的,因为这些过程也是耗费时间的,这个时候我们就需要用到线程池了,我们只要往池子里放任务,他就会自动帮你管理线程的创建与销毁,利用缓存复用减少线程销毁创建,优化系统性能。以下是线程池的优点:

  1. 通过对线程进行缓存,减少了创建销毁的时间损失
  2. 通过控制线程数量阀值,减少了当线程过少时带来的CPU闲置(比如说长时间卡在I\O上了)与线程过多时对JVM的内存与线程切换压力
    而Dispatcher内部的核心即线程池
线程池初始化
  • int corePoolSize: 最小并发线程数,这里并发同时包括空闲与活动的线程,如果是0的话,空闲一段时间后所有线程将全部被销毁。
  • int maximumPoolSize: 最大线程数,当任务进来时可以扩充的线程最大值,当大于了这个值就会根据丢弃处理机制来处理
  • long keepAliveTime: 当线程数大于corePoolSize时,多余的空闲线程的最大存活时间,类似于HTTP中的Keep-alive
  • TimeUnit unit: 时间单位,一般用秒
  • BlockingQueue workQueue: 工作队列
  • ThreadFactory threadFactory: 单个线程的工厂,可以打Log,设置Daemon(即当JVM退出时,线程自动结束)等
反向代理模式

为了解决单生产者多消费者问题,OkHttp采用了反向代理模式,来解决非阻塞,高并发问题

OkHttp工作模式

Dispatch模式

  • int corePoolSize: 最小并发线程数,这里并发同时包括空闲与活动的线程,如果是0的话,空闲一段时间后所有线程将全部被销毁。
  • int maximumPoolSize: 最大线程数,当任务进来时可以扩充的线程最大值,当大于了这个值就会根据丢弃处理机制来处理
  • long keepAliveTime: 当线程数大于corePoolSize时,多余的空闲线程的最大存活时间,类似于HTTP中的Keep-alive
  • TimeUnit unit: 时间单位,一般用秒
  • BlockingQueue workQueue: 工作队列
  • ThreadFactory threadFactory: 单个线程的工厂,可以打Log,设置Daemon(即当JVM退出时,线程自动结束)等
    OkHttp内Dispatcher原理
    其实当我们调用client.newCall(request).enqueue(new callback(){...})的时候实质是
Dispatcher入队(maxRequests=64,maxRequestsPerHost=5)

此时判断线程池内有无空闲,否则进入等待队列,AsyncCall实质是Runnable,当任务执行完毕后,总会调用finally{
client.dispatcher().finished(this);}清除此任务。
我们回头看看OkhttpClient初始化内其它一些参数Expires,Cache-Control,Last-Modified,If-Modified-Since,ETag,If-None-Match...这些都牵扯到缓存问题,大概对于缓存的判断过程入下图


浏览器缓存机制-[吴秦(Tyler)](http://www.cnblogs.com/skynet/)

okhttp内部是通过转化实现以上机制


okhttp内部缓存map操作

参数解释:
request:这些头参数会被okhttp的流程装置Interceptor打断(下面会讲),组装成新的request
cacheCandidate:上次与服务器交互的缓存的Response,基于文件系统的Map,key为url的md5,置换算法为lru,即带缓存Header的Response。

请求参数不同下返回状况

具体内部实现为CacheStrategy,有兴趣的可以自己看下源码
接下来讲讲上面提到的流程工具Interceptor

Interceptor

从上图看出主要用于两个地方,对request进行包装,或者对response进行解析,Interceptor可以用来监控log,修改请求,修改结果,还有GZIP压缩。

对于request加工过程
  1. 加工过程是一个自增递归调用过程,直到将拦截器全部调用完。(header加一个map就生成一个拦截器)
  2. 处理完后,获得网络请求,getResponse(),将按照http协议规范重新序列化对象中的数据信息,最终为Raw文本
获得Response后的加工

总体是一个将Socket解析为Response对象的过程

  1. 读取Raw,反序列化为Statusline对象
  2. 以Transfer-Encoding:chunked的模式传输并组装Body(此处不太明白)
    接下来看看Call,Call相对比较简单是个接口,RealCall为其实现类主要实现一个桥梁作用,连接okhttpclient与request最后我们要进行真正的网络请求了,就要涉及到网络路径选择,这又是okhttp的另一大优势
    原来的http链接我们知道是单次传输,结束后就不再通信了,对于网络请求比较频繁的操作,这种方式貌似比较浪费时间,中间需要不断的建立连接,因此我们需要socket那种一直保持连接的方式,所幸Http有keepalive属性,直接保持连接

OkHttp中会保持5个并发,时间为5分钟,当然这些数字是经过考量,测试得出的

  1. 首先因为服务器的带宽是有限定的,他能提供的服务也是有限的,就像银行办事,可以开很多个窗口,但是银行员工就那么几个,你要是站着茅坑不拉屎,势必影响整个银行的办事效率
  2. 服务器/防火墙一般都会有并发限制,毕竟这个就跟硬件搭界了
  3. 当然不排除黑客用僵尸连接一直占着你服务器资源

这里对于连接的控制主要由Connection(连接主要对象),StreamAllocation计数器(实现回收策略关键),ConnectionPool连接池以及Deque构成

  1. 首先Connection是个接口,他的实现类是RealConnection,这个对象保存了连接的信息,比如,核心Socket对象,Handshake握手信息,Protocol信息...是整个请求的核心部分,也是管理单元
  2. 其次StreamAllocation是个计数器,用来记录Connection被引用的次数,以便我们在清理整个ConnectionPool的时候可根据最少使用原则,将引用置换或者清理
  3. Dequeue用来存放RealConnection
  4. ConnectionPool是管理Connection的集合,提供类基本的管理类该有的功能,其中包含一个线程池来定时执行清理任务cleanup(long now)具体清理过程有别于GC的线索查找,即上述的计数器原理,而cleanup内又有pruneAndGetAllocationCount函数用来扫描当前Dequeue内Connection的状况,并返回等待时间(主要条件即空闲Socket>5&&keepalive<5)

由于个人项目网络缓存是自己写的,更好控制,okhttp的缓存策略也就不深入研究了,不过大家可以了解下Okio这个库,是个对文件I/O封装很好的库,如果有兴趣的同学可以参考
文/BlackSwift(简书作者)
原文链接:http://www.jianshu.com/p/aad5aacd79bf
再次感谢该作者的4篇文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容