建造者模式
建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
使用场景:
- 当产品有复杂的内部构造时(参数很多);
- 需要控制构建顺序(属性赋值顺序)时。
主要构成:
- 目标类:最终生成的对象的类。
- Builder类:包含目标类所有成员变量,并提供Getter/Setter方法。并提供成员变量的默认值。在biuld()中定义成员变量赋值顺序。
OkHttp对象
封装了所有暴露给用户的接口。包括配置各种参数和发出请求。
使用了建造者模式(属于参数很多的场景),在OkHttpClient.Builder().build()中:
- 创建Dispatcher对象;负责分发请求对象。
- 创建Proxy对象;封装了“代理设置”,有“直连”、“Http代理”、“Socket代理”三种,并且封装了Socket地址。
- 创建Protocol对象集合;有Http2和Http1.1两种。
- 创建ConnectSpec对象集合;
- 创建Interceptor对象集合interceptors;
- 创建Interceptor对象集合networkInterceptors;
- 创建EventListener.Factory对象;管理一个网络请求过程中各个关键点的回调监听。
- 创建ProxySelector对象;
- 创建CookieJar对象;提供Cookie管理策略。
- 创建Cache对象;负责缓存响应。
- 创建InternalCache对象;负责OkHttp内部缓存,应用层不需要关心。
- 创建SocketFactory对象;创建Socket对象的工厂类。
- 创建SSLSocketFactory对象;创建SSLSocket对象的工厂类
- 创建CertificateChainCleaner对象;
- 创建HostnameVerifier对象;负责“主机名校验”。
- 创建CertificatePinner对象;
- 创建Authenticator对象;
- 创建ConnectionPool对象;负责管理“连接池”。
- 创建Dns对象;
- 设置是否允许重定向、是否允许重试、连接超时、写超时、读超时。
Request对象
Request对象封装一次请求的所有参数。
使用了建造者模式,在Request.Builder().build()中:
- 创建HttpUrl对象;
- 设置请求方式;
- 创建Headers对象;
- 创建RequestBody对象;
- 设置tag。
随后,创建RealCall对象。RealCall对象封装了OkHttpClient对象、Request对象,实现了Call接口,对外提供同步发送请求的方法execute()和异步发送请求的方法enqueue()。
Dispatcher
Dispatcher负责分发请求对象。封装了3个Deque,readyAsyncCalls管理待执行的异步请求,runningAsyncCalls管理正在执行的异步请求,runningSyncCalls管理正在执行的同步请求。封装了请求线程池(ThreadPoolExecutor)、空闲回调(idleCallback,当Dispatcher没有任何请求对象时调用)。
分发同步请求
- 把请求加入runningSyncCalls;
- 调用getResponseWithInterceptorChain()获取响应对象;
- 从runningSyncCalls中删除请求对象,判断是否需要执行idleCallback。
分发异步请求
- 把RealCall对象和Callback对象封装成AsyncCall对象;
- AsyncCall对象是一个Runnable对象;
- 调用Dispatcher对象的同步方法enqueue(),如果runningAsyncCalls数量达到全局最大请求数(64)、或达到主机最大请求数(5)时,把AsyncCall对象加入readyAsyncCalls;否则加入runningAsyncCalls。
- 请求线程池执行AsyncCall对象,也就是转调AsyncCall对象的execute();
- 调用getResponseWithInterceptorChain()获取响应对象;
- 判断是否在请求过程中执行了“取消”操作,如果是,则回调Callback对象的onFailure(),否则回调onResponse();
- 从runningAsyncCalls中删除请求对象;
- 迭代readyAsyncCalls,把下一个AsyncCall对象加入runningAsyncCalls,并让请求线程池执行它;
- 判断是否需要执行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对象,并发起请求,直到不需要重试或重定向。
- 当响应码是300、301、302、303时,即需要重定向;
- 判断OkHttpClient对象的设置,是否允许重定向;
- 读取响应报头的Location字段,并构造HttpUrl对象;
- 判断是否跨域名、OkHttpClient对象是否允许跨域名重定向;
- 构造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():储存的实现
- 如果请求方法是“Post“、”Patch“、”Put“、“Delete”、“Move”,那么缓存的请求已经不能再用了,于是删除缓存;
- 如果请求方法不是“Get”,不进行缓存;
- 对请求的url进行UTF-8编码,然后md5,再hex,作为键;
- 在LinkedHashMap中查找键对应的Entry对象,找不到的话创建Entry对象存入LinkedHashMap;
- 1个Entry对象对应4个File对象;2个cleanFile(0 + key + .tmp 和1 + key + .tmp),2个dirtyFile(0 + key 和1 + key);序号0表示记录的是MetaData,序号1表示记录的是Body;
- 向0 + key File写入请求报头、响应报头;向1 + key File写入响应报文;
- 如果在写入的过程中没有干扰,即成功写入了全部内容,那么dirtyFile将被重命名为相应的cleanFile。
get():读取的实现
- 对请求的url进行UTF-8编码,然后md5,再hex,作为键;
- 按键在LinkedHashMap中查找Entry对象;
- 创建Entry对象中cleanFile对应的Source对象;
- 由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对象。