Android网络编程(七)源码解析OkHttp前篇[请求网络]

前言

学会了OkHttp3的用法后,我们当然有必要来了解下OkHttp3的源码,当然现在网上的文章很多,我仍旧希望我这一系列文章篇是最简洁易懂的。

1.从请求处理开始分析

首先OKHttp3如何使用这里就不在赘述了,不明白的同学可以查看Android网络编程(五)OkHttp用法全解析Android网络编程(六)OkHttp3用法全解析这两篇文章。当我们要请求网络的时候我们需要用OkHttpClient.newCall(request)进行execute或者enqueue操作,当我们调用newCall时:

实际返回的是一个RealCall类,我们调用enqueue异步请求网络实际上是调用了RealCall的enqueue方法:

可以看到最终的请求是dispatcher来完成的。

2.Dispatcher任务调度

主要的变量

Dispatcher主要用于控制并发的请求,它主要维护了以下变量:

构造函数

Dispatcher有两个构造函数,可以使用自己设定线程池,如果没有设定线程池则会在请求网络前自己创建线程池,这个线程池类似于CachedThreadPool比较适合执行大量的耗时比较少的任务。

异步请求

当正在运行的异步请求队列中的数量小于64并且正在运行的请求主机数小于5时则把请求加载到runningAsyncCalls中并在线程池中执行,否则就再入到readyAsyncCalls中进行缓存等待。

AsyncCall

线程池中传进来的参数就是AsyncCall它是RealCall的内部类,内部也实现了execute方法:

首先我们来看看最后一行, 无论这个请求的结果如何都会执行client.dispatcher().finished(this);

finished方法将此次请求从runningAsyncCalls移除后还执行了promoteCalls方法:

可以看到最关键的点就是会从readyAsyncCalls取出下一个请求,并加入runningAsyncCalls中并交由线程池处理。好了让我们再回到上面的AsyncCall的execute方法,我们会发getResponseWithInterceptorChain方法返回了Response,很明显这是在请求网络。

3.Interceptor拦截器

getResponseWithInterceptorChain方法,创建了ApplicationInterceptorChain,它是一个拦截器链,这个类也是RealCall的内部类,接下来执行了它的proceed方法:

proceed方法每次从拦截器列表中取出拦截器,当存在多个拦截器时都会在第七行阻塞,并等待下一个拦截器的调用返回。下面分别以 拦截器链中有1个、2个拦截器的场景加以模拟:

拦截器主要用来观察,修改以及可能短路的请求输出和响应的回来。通常情况下拦截器用来添加,移除或者转换请求或者响应的头部信息。比如将域名替换为ip地址,将请求头中添加host属性,也可以添加我们应用中的一些公共参数,比如设备id、版本号等等。

回到代码上来,我们看最后一行 return getResponse(request, forWebSocket),如果没有更多的拦截器的话,就会执行网络请求,来看看getResponse方法做了些什么(RealCall.Java):

getResponse方法比较长我省略了一些代码,可以看到创建了HttpEngine类并且调用HttpEngine的sendRequest方法和readResponse方法。

4.缓存策略

我们先来看看sendRequest方法:

上面的代码显然是在发送请求,但是最主要的是做了缓存的策略。cacheCandidate是上次与服务器交互缓存的Response,这里的缓存都是基于Map,key是请求中url的md5,value是在文件中查询到的缓存,页面置换基于LRU算法,我们现在只需要知道它是一个可以读取缓存Header的Response即可。根据cacheStrategy的处理得到了networkRequest和cacheResponse这两个值,根据这两个值的数据是否为null来进行进一步的处理,当networkRequest和cacheResponse都为null的情况也就是不进行网络请求并且缓存不存在或者过期,这时候则返回504错误;当networkRequest 为null时也就是不进行网络请求,而且缓存可以使用时则直接返回缓存;其他的情况则请求网络。 接下来我们查看readResponse方法:

这个方法发起刷新请求头部和请求体,解析HTTP响应头部。如果有缓存并且可用则用缓存的数据并更新缓存,否则就用网络请求返回的数据。 我们再来看看validate(cacheResponse, networkResponse)方法是如何判断缓存是否可用的:

如缓存果过期或者强制放弃缓存,在此情况下,缓存策略全部交给服务器判断,客户端只用发送条件get请求即可,如果缓存是有效的,则返回304 Not Modifiled,否则直接返回body。条件get请求有两种方式一种是Last-Modified-Date,一种是 ETag。这里采用了Last-Modified-Date,通过缓存和网络请求响应中的Last-Modified来计算是否是最新数据,如果是则缓存有效。

5.失败重连

最后我们再回到RealCall的getResponse方法:

查看代码第11行和21行当发生IOException或者RouteException时会执行HttpEngine的recover方法:

最后一行可以看到就是重新创建了HttpEngine并返回,用来完成重连。 到这里OkHttp请求网络的流程基本上讲完了,下面是关于OKHttp的请求流程图:

本文分享自微信公众号 - 刘望舒(liuwangshuAndroid)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-08-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏快乐八哥

探讨Android中的内置浏览器和Chrome

1.Android默认浏览器和Chrome的区别 Android出厂自带的浏览器:安卓WebKit浏览器,也成内置浏览器或者默认浏览器。 安卓WebKit不是C...

34190
来自专栏张善友的专栏

使用View Model从表现层分离领域模型

Model-View-Controller(模型-视图-控制器,MVC) 模式将你的软件组织并分解成三个截然不同的角色: Model 封装了你的应用数据、应用...

26050
来自专栏张善友的专栏

Visual Studio 2015正式发布

Windows 10 RTM正式版要7月29日发布,微软的另一个重磅软件Visual Studio 2015已经率先发布,今天如期放出了正式版本。Visual ...

25890
来自专栏菩提树下的杨过

rpc框架之 avro 学习 2 - 高效的序列化

同一类框架,后出现的总会吸收之前框架的优点,然后加以改进,avro在序列化方面相对thrift就是一个很好的例子。借用Apache Avro 与 Thrift ...

34360
来自专栏快乐八哥

使用jQuery UI的draggable和droppable完成拖拽功能--介绍

第一部分--拖拽介绍 在https://code.csdn.net/2013ossurvey中最后一个开源项目就是zTree,一方面是因为自己看到有项目中使用了...

32950
来自专栏张善友的专栏

使用密码记录工具keepass来保存密码

在第一章,曾经给过您建议,密码不要保存在文档中,那样不安全,如果密码很多而且又很复杂,人的大脑是不可能很容易记住的,只能记录下来,如果不能记在文档中那记在哪里呢...

51190
来自专栏偏前端工程师的驿站

动手写个数字输入框1:input[type=number]的遗憾

前言  最近在用Polymer封装纯数字的输入框,开发过程中发现不少坑,也有很多值得研究的地方。本系列打算分4篇来叙述这段可歌可泣的踩坑经历: 《动手写个数字输...

31850
来自专栏快乐八哥

Progressive Web Apps入门

PC和Mobile开发技术演进 PC方向,从客户端到富客户端,到现在广泛使用的Web。 移动方向,目前主要还是原生应用和Mobile Web,PWA相关技术是未...

277100
来自专栏张善友的专栏

TCP/IP, WebSocket 和 MQTT

按照OSI网络分层模型,IP是网络层协议,TCP是传输层协议,而HTTP和MQTT是应用层的协议。在这三者之间, TCP是HTTP和MQTT底层的协议。大家对H...

1.8K90
来自专栏菩提树下的杨过

如何在mac本上安装android sdk

众所周知的原因,google的很多网站在国内无法访问,苦逼了一堆天朝程序员,下是在mac本上折腾android 开发环境的过程: 一、先下载android sd...

227100

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励