OkHttp

OkHttp

Posted by candy1126xx on May 6, 2017

建造者模式

建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

使用场景:

  • 当产品有复杂的内部构造时(参数很多);
  • 需要控制构建顺序(属性赋值顺序)时。

主要构成:

  • 目标类:最终生成的对象的类。
  • Builder类:包含目标类所有成员变量,并提供Getter/Setter方法。并提供成员变量的默认值。在biuld()中定义成员变量赋值顺序。

OkHttp对象

封装了所有暴露给用户的接口。包括配置各种参数和发出请求。

使用了建造者模式(属于参数很多的场景),在OkHttpClient.Builder().build()中:

  1. 创建Dispatcher对象;负责分发请求对象。
  2. 创建Proxy对象;封装了“代理设置”,有“直连”、“Http代理”、“Socket代理”三种,并且封装了Socket地址。
  3. 创建Protocol对象集合;有Http2和Http1.1两种。
  4. 创建ConnectSpec对象集合;
  5. 创建Interceptor对象集合interceptors;
  6. 创建Interceptor对象集合networkInterceptors;
  7. 创建EventListener.Factory对象;管理一个网络请求过程中各个关键点的回调监听。
  8. 创建ProxySelector对象;
  9. 创建CookieJar对象;提供Cookie管理策略。
  10. 创建Cache对象;负责缓存响应。
  11. 创建InternalCache对象;负责OkHttp内部缓存,应用层不需要关心。
  12. 创建SocketFactory对象;创建Socket对象的工厂类。
  13. 创建SSLSocketFactory对象;创建SSLSocket对象的工厂类
  14. 创建CertificateChainCleaner对象;
  15. 创建HostnameVerifier对象;负责“主机名校验”。
  16. 创建CertificatePinner对象;
  17. 创建Authenticator对象;
  18. 创建ConnectionPool对象;负责管理“连接池”。
  19. 创建Dns对象;
  20. 设置是否允许重定向、是否允许重试、连接超时、写超时、读超时。

Request对象

Request对象封装一次请求的所有参数。

使用了建造者模式,在Request.Builder().build()中:

  1. 创建HttpUrl对象;
  2. 设置请求方式;
  3. 创建Headers对象;
  4. 创建RequestBody对象;
  5. 设置tag。

随后,创建RealCall对象。RealCall对象封装了OkHttpClient对象、Request对象,实现了Call接口,对外提供同步发送请求的方法execute()和异步发送请求的方法enqueue()。

Dispatcher

Dispatcher负责分发请求对象。封装了3个Deque,readyAsyncCalls管理待执行的异步请求,runningAsyncCalls管理正在执行的异步请求,runningSyncCalls管理正在执行的同步请求。封装了请求线程池(ThreadPoolExecutor)、空闲回调(idleCallback,当Dispatcher没有任何请求对象时调用)。

分发同步请求

  1. 把请求加入runningSyncCalls;
  2. 调用getResponseWithInterceptorChain()获取响应对象;
  3. 从runningSyncCalls中删除请求对象,判断是否需要执行idleCallback。

分发异步请求

  1. 把RealCall对象和Callback对象封装成AsyncCall对象;
  2. AsyncCall对象是一个Runnable对象;
  3. 调用Dispatcher对象的同步方法enqueue(),如果runningAsyncCalls数量达到全局最大请求数(64)、或达到主机最大请求数(5)时,把AsyncCall对象加入readyAsyncCalls;否则加入runningAsyncCalls。
  4. 请求线程池执行AsyncCall对象,也就是转调AsyncCall对象的execute();
  5. 调用getResponseWithInterceptorChain()获取响应对象;
  6. 判断是否在请求过程中执行了“取消”操作,如果是,则回调Callback对象的onFailure(),否则回调onResponse();
  7. 从runningAsyncCalls中删除请求对象;
  8. 迭代readyAsyncCalls,把下一个AsyncCall对象加入runningAsyncCalls,并让请求线程池执行它;
  9. 判断是否需要执行idleCallback。

责任链模式

责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象能够处理它。

使用场景:当1个对象需要经过多个对象处理时。

主要构成:

  • 1个流程控制接口,其中声明2个方法,1个用于处理对象,1个用于控制流程;
  • 多个拦截器类,都实现了流程控制接口的“处理”方法;
  • 1个流程节点类,实现了流程控制接口,封装了拦截器对象;在“处理”方法中转调拦截器的“处理”方法,然后调用“控制流程”方法;在“控制流程”方法中调用封装了下一个拦截器对象的流程节点对象的“处理”方法。

OkHttp中的责任链

流程控制接口

OkHttp中的流程控制接口分成了2个,Interceptor接口声明了“处理”方法intercept()、Interceptor.Chain接口声明了“控制流程”方法proceed()。

拦截器类

OkHttp中默认实现的拦截器类有RetryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor、ConnectInterceptor、CallServerInterceptor。

流程节点类

RealInterceptorChain。它实现了Interceptor.Chain的proceed(),对拦截器集合进行迭代,构造出下一个RealInterceptorChain对象,并调用对应拦截器对象的intercept()。

调用流程

发起请求,对Request对象处理:用户实现的拦截器的proceed() -> RetryAndFollowUpInterceptor.proceed() -> BridgeInterceptor.proceed() -> CacheInterceptor.proceed() -> ConnectInterceptor.proceed() -> CallServerInterceptor.proceed(),在其中构造了Response对象,然后一路return:ConnectInterceptor.proceed() -> CacheInterceptor.proceed() -> BridgeInterceptor.proceed() -> RetryAndFollowUpInterceptor.proceed() -> 用户实现的拦截器的proceed();

RetryAndFollowUpInterceptor

功能

仅处理Response对象,进行“重试”和“重定向”。

原理

在proceed()的实现中是1个while死循环,在循环中由Response对象构造新的Request对象,并发起请求,直到不需要重试或重定向。

  1. 当响应码是300、301、302、303时,即需要重定向;
  2. 判断OkHttpClient对象的设置,是否允许重定向;
  3. 读取响应报头的Location字段,并构造HttpUrl对象;
  4. 判断是否跨域名、OkHttpClient对象是否允许跨域名重定向;
  5. 构造Request对象,并发起请求。

BridgeInterceptor

功能

  • Request:处理Request对象的报头;读取Cookie。
  • Response:与Request对象进行关联;缓存Cookie;进行Gzip解压。

CacheInterceptor

功能

  • Request:由Request对象查询缓存,如果有缓存可用,直接返回由缓存构造的Resopnse对象。
  • Response:缓存Get请求的响应。

原理

InternalCache对象是缓存操作的具体实现类。InternalCache对象中封装了1个DiskLruCache对象。DiskLruCache对象中封装了1个accessOrder为true(访问排序)的LinkedHashMap。

put():储存的实现
  1. 如果请求方法是“Post“、”Patch“、”Put“、“Delete”、“Move”,那么缓存的请求已经不能再用了,于是删除缓存;
  2. 如果请求方法不是“Get”,不进行缓存;
  3. 对请求的url进行UTF-8编码,然后md5,再hex,作为键;
  4. 在LinkedHashMap中查找键对应的Entry对象,找不到的话创建Entry对象存入LinkedHashMap;
  5. 1个Entry对象对应4个File对象;2个cleanFile(0 + key + .tmp 和1 + key + .tmp),2个dirtyFile(0 + key 和1 + key);序号0表示记录的是MetaData,序号1表示记录的是Body;
  6. 向0 + key File写入请求报头、响应报头;向1 + key File写入响应报文;
  7. 如果在写入的过程中没有干扰,即成功写入了全部内容,那么dirtyFile将被重命名为相应的cleanFile。
get():读取的实现
  1. 对请求的url进行UTF-8编码,然后md5,再hex,作为键;
  2. 按键在LinkedHashMap中查找Entry对象;
  3. 创建Entry对象中cleanFile对应的Source对象;
  4. 由Source对象读取文件内容构造Response对象。
update():更新的实现

ConnectInterceptor

功能

仅在Request有用;通过StreamAllocation对象获取可用的RealConnection对象和HttpCodec对象。

管理连接池

RealConnection对象代表一个可用或曾经可用的连接,由ConnectionPool对象管理。ConnectionPool对象封装了1个RealConnection的Deque(connections)和1个RouteDatabase对象。

CallServerInterceptor

功能

  • Request:用HttpCodec对象向Socket流写入Request对象。
  • Response:用HttpCodec对象从Socket流读出Response对象。

写入Request对象

读出Response对象