一、前言:

1.1 本篇主要讲解内容

1.责任链模式介绍

2.OKHttp中责任链的调用原理

3.五个拦截器以及两个扩展类拦截器

4.如何使用扩展类拦截器

5.实际场景下扩展类拦截器的使用

1.2 OKHttp项目地址:

https://github.com/square/okhttp

1.3 OKHttp系列讲解简介:

OKHttp项目本身还是很大的,而且涉及到的知识点很多,所以一篇文章是很难概括全的,所以会以一个系列文章的方式来详细讲解OKHttp。

系列准备写以下几篇。

OKHttp原理讲解之基本概念_失落夏天的博客-CSDN博客

OKHttp原理讲解之责任链模式及扩展(预计03.18发布)

OKHttp原理讲解之重试拦截器(预计03.22发布)

OKHttp原理讲解之路由拦截器

OKHttp原理讲解之缓存拦截器

OKHttp原理讲解之连接池拦截器

OKHttp原理讲解之请求拦截器

PS:预计每周更新1-2篇的进度进行

二、责任链模式介绍

2.1什么是责任链模式

责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

2.2 责任链在安卓中的使用

责任链模式在安卓中的使用虽然没有特别的多,但是也是挺广泛的,除了OKHttp,事件分发,ViewGroup的层层渲染使用的也是典型的责任链模式。

三、拦截器简介

3.1拦截器执行顺序

OKHttp中,一共有5个自带的拦截器,以及2个集合可扩展的拦截器(集合中可以有多个拦截器)。

整个责任链调用顺序如下:

自定义拦截器集合->RetryAndFollowUpInterceptor->BridgeInterceptor->CacheInterceptor->

ConnectInterceptor->网络拦截器集合->CallServerInterceptor。

最终实现的代码如下:

    List<Interceptor> interceptors = new ArrayList<>();interceptors.addAll(client.interceptors());interceptors.add(retryAndFollowUpInterceptor);interceptors.add(new BridgeInterceptor(client.cookieJar()));interceptors.add(new CacheInterceptor(client.internalCache()));interceptors.add(new ConnectInterceptor(client));if (!forWebSocket) {interceptors.addAll(client.networkInterceptors());}interceptors.add(new CallServerInterceptor(forWebSocket));

3.2拦截器简介

5个OKHttp自带的拦截器如下:

RetryAndFollowUpInterceptor:主要负责重试,跟踪请求。以及主要控制发送请求的重试次数。

BridgeInterceptor:主要负责拼接header,以及收到内容后去解析数据得到最终的响应。

CacheInterceptor:缓存连接器顾名思义,就是用来装载缓存的。

ConnectInterceptor:连接池拦截器,顾名思义,用来对连接池进行处理。

CallServerInterceptor:请求拦截器。主要负责最终请求的发送和接收。

两个自定义拦截器集合

interceptors:在拦截器集合中排在第一位,我们可以使用这个拦截器对最终返回的数据再次进行处理。比如数据解密等等。

networkInterceptors:在拦截器集合中排在连接池拦截器和请求拦截器之间,可以对收到的最原始的报文进行处理。

四、OKHttp中责任链的使用原理

4.1责任链调用原理

首先,所有的拦截器都实现Interceptor中接口,接口中只有一个方法intercept()。每个拦截器都通过这个方法通知到下一层,也是通过这个方法的返回值接受来自下一层级的响应。

public interface Interceptor {Response intercept(Chain chain) throws IOException;...
}

二,请求开始流程开始。请求流程中会先构建一个List集合,这个List集合中装载了所使用到的所有的interceptor拦截器。代码如下:

    List<Interceptor> interceptors = new ArrayList<>();interceptors.addAll(client.interceptors());interceptors.add(retryAndFollowUpInterceptor);interceptors.add(new BridgeInterceptor(client.cookieJar()));interceptors.add(new CacheInterceptor(client.internalCache()));interceptors.add(new ConnectInterceptor(client));if (!forWebSocket) {interceptors.addAll(client.networkInterceptors());}interceptors.add(new CallServerInterceptor(forWebSocket));

三,构建链表对象Chain,其实现类为RealInterceptorChain。RealInterceptorChain实现了Interceptor.Chain的接口。Chain接口中有很多方法,其中proceed方法负责流程的向下分发。

RealInterceptorChain构造方法中有很多参数,我们这里只看interceptors和index,interceptors代表所有的拦截器,index代表执行到第几个。可以这么理解,Chain负责调度,interceptor负责执行。

public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {this.interceptors = interceptors;this.connection = connection;this.streamAllocation = streamAllocation;this.httpCodec = httpCodec;this.index = index;this.request = request;this.call = call;this.eventListener = eventListener;this.connectTimeout = connectTimeout;this.readTimeout = readTimeout;this.writeTimeout = writeTimeout;}

四,调用proceed方法开始责任链流程。我们看RealInterceptorChain中的proceed方法:

该方法中,index+1,构建下一层级的chain对象。

然后获取当前的拦截器,因为此时的index=0,所以对应的拦截器是RetryAndFollowUpInterceptor,则会调用其中的intercept()方法。

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection) throws IOException {...// Call the next interceptor in the chain.RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,writeTimeout);Interceptor interceptor = interceptors.get(index);Response response = interceptor.intercept(next);...return response;}

五,intercept方法中。通过上面创建的chain对象,使用其proceed方法继续向下分发。

我们这里以RetryAndFollowUpInterceptor中的intercept方法为例。可以看到通过上面一步构建的Chain对象继续向下一层分发,同时获取response返回值,进行一定的处理后,最终返回response。其他的intercept流程上也是一样的。

public Response intercept(Chain chain) throws IOException {Request request = chain.request();RealInterceptorChain realChain = (RealInterceptorChain) chain;...response = realChain.proceed(request, streamAllocation, null, null);...if (followUp == null) {if (!forWebSocket) {streamAllocation.release();}return response;}...}}

六,Interceptor一层一层的向下分发,执行到最后一层拦截器CallServerInterceptor时,由于不需要继续向下分发,所以可以是直接返回response的。

CallServerInterceptor中的intercept()的方法如下:

public Response intercept(Chain chain) throws IOException {RealInterceptorChain realChain = (RealInterceptorChain) chain;HttpCodec httpCodec = realChain.httpStream();StreamAllocation streamAllocation = realChain.streamAllocation();RealConnection connection = (RealConnection) realChain.connection();Request request = realChain.request();long sentRequestMillis = System.currentTimeMillis();...Response response = responseBuilder.request(request).handshake(streamAllocation.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();...return response;}

五、自定义拦截器的使用

添加方式也很简单:

我们首先生成一个拦截器,然后实现其intercept接口。

这个接口中我们除了我们的自定义逻辑,还需要实现其责任链的功能:

return chain.proceed(chain.request())

1.添加自定义拦截器

 client.interceptors().add(object : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {//...这里我们可以实现一些自定义逻辑return chain.proceed(chain.request())}})

2.添加自定义网络拦截器

 client.networkInterceptors().add(object : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {//...这里我们可以实现一些自定义逻辑return chain.proceed(chain.request())}})

六、实际场景下扩展类拦截器的使用

场景一:

这里在举一个实际的场景例子。需求如下:

我们服务端返回的数据是加密的,但是对于应用层逻辑来说,自然是希望直接使用解密后的数据。这就需要使用到我们的应用拦截器。我们在收到response之后,获取返回的字节进行解密操作,然后重新构建一个response返回给逻辑层。

builder.addInterceptor(object : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val response = chain.proceed(chain.request())val body = response.body()!!val bytes = body.bytes()val newBytes = decrypt(bytes)//解密转换val buffer = Buffer()buffer.write(newBytes)val newBody =RealResponseBody(body.contentType()!!.type() + File.separator + body.contentType()!!.subtype(),body.contentLength(),buffer)val responseBuilder = response.newBuilder()responseBuilder.body(newBody)val newRresponse = responseBuilder.build()return newRresponse}})

场景二:

这里在举一个实际的场景例子。需求如下:

我们首页中有一块区域展示一个清单列表,这一块的数据来源于服务。这个清单,有的用户是关心的,有的用户是不关心的。为了追求首页快速加载,所以我们希望首次使用缓存,对于那些关心这块列表的人,他们自然会下拉刷新,这时候我们就希望不是用缓存而是直接去发请求,等到收到响应后替换原有的缓存,因为最新的返回值时间上更新一些。

那我们该如何去实现这需求呢?

首先,我们要区分是否是用缓存。那这个简单,构建reqeust的时候,我们就可以通过CacheControl控制该请求是否使用缓存。

        val builder = Request.Builder()val cacheBuilder = CacheControl.Builder()cacheBuilder.noStore()cacheBuilder.noStore()builder.cacheControl(cacheBuilder.build())val request = builder.url("https://www.baidu.com").build()

当然们还可以使用另外一种更简单的方式来实现,因为缓存是否存在的key是url,那我们如果不想使用缓存时,添加一些标记位来区分。比如正常请求https://www.server.com/api,不是用缓存的请求URL:https://www.server.com/api?cache=123。

然后来到第二个需求点,希望不使用缓存的请求,也要把响应更新到缓存中,这个怎么办呢?

这时候就轮到我们自定义拦截器上场了。假设我们使用上面的第二种场景,我们可以在发送完请求收到响应后,修改request中的url,把url改回:https://www.server.com/api。这样的话,继续向上返回走到缓存拦截器,则就会更新其缓存。代码如下:

client.networkInterceptors().add(object : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {var response = chain.proceed(chain.request())val request = chain.request()val url = request.url()val host = url.host()//判断如果host是带缓存的,则把host中的标记位消除掉if (host.endsWith("?cache=1")) {val urlBuilder = url.newBuilder()urlBuilder.host(host.replace("?cache=1", ""))val requestBuilder = request.newBuilder()requestBuilder.url(urlBuilder.build())val responseBuilder = response.newBuilder()responseBuilder.request(requestBuilder.build())response = responseBuilder.build()}return response}})

整个流程如下:

使用缓存场景:

请求URL:https://www.server.com/api?cache=1,走到缓存拦截器。缓存拦截器中有该请求的响应,则直接返回响应。

不是用缓存场景:

请求URL:https://www.server.com/api,走到缓存拦截器。缓存拦截器中没有该请求的响应,则发送请求。收到响应后,把请求地址改为https://www.server.com/api?needcache=true。则回到缓存拦截器的时候,就会把https://www.server.com/api?needcache=true当做key更新到缓存中。

OKHttp原理讲解之责任链模式及扩展相关推荐

  1. OKHttp原理讲解之RetryAndFollowUpInterceptor

    一.前言: 1.1 本篇主要讲解内容 1.RetryAndFollowUpInterceptor中主要成员介绍 2.拦截器中重试机制 3.拦截器中执行流程 1.2 OKHttp项目地址: https: ...

  2. Netty框架之责任链模式及其应用

    Netty框架之概述及基本组件介绍 Reactor网络编程模型解析 前言 在上篇博客介绍完netty框架的基本组件介绍和概述,也跟着代码看了下NioEventLoopGroup的启动过程,以及基于Re ...

  3. 责任链模式实践之Zuul责任链模式

    责任链模式实践之Zuul责任链模式 一,什么是责任链模式 责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对 ...

  4. 责任链模式(doFilter)的123、321原理

    doFile可以处理request,当然也可以处理response. 但是顺序有点绕,处理request的时候是按照正常的顺序执行,处理response的时候跟request的处理是相反的.原理是啥? ...

  5. 设计模式(四)OkHttp的责任链模式

    一.基本概念 1.定义 多个对象都有机会处理请求,将这些对象连成一个链,将请求沿着这条链传递,直到有对象处理为止. 2.使用场景 多个对象处理同一请求,具体哪个对象处理需要动态决定 需要指定一组对象处 ...

  6. 【设计模式系列19】状态模式原理分析及其和策略模式,责任链模式的区别

    状态模式原理分析 设计模式系列总览 前言 什么是状态模式 状态模式示例 状态模式角色 状态模式与责任链模式 状态模式与策略模式 状态模式应用场景 状态模式优缺点 总结 设计模式系列总览 设计模式 飞机 ...

  7. 23种设计模式之——责任链模式(okhttp 拦截器)

    前言 网络七层协议 在现实中的责任链模型之一就是网络连接.对与程序猿而言,七层或五层的网络连接模型是肯定知道的. 当一个网络请求发出时,需要经过应用层->传输层->网络层->连接层- ...

  8. spring AOP原理分析:静态代理;JDK实现接口动态代理;Cglib继承父类代理;SpringAop的责任链模式调用

    普通静态代理 代理类和真实类都需要实现同一个接口 接口 package com.fchan.layui.represent.service; /*** 静态代理demo*/ public interf ...

  9. 字节面试:什么是责任链模式?

    微信搜索[三太子敖丙]关注这个贪财好色的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系列文章. 前言 面试经历 ...

  10. 轻松学习Java设计模式之责任链模式

    我们的态度是:每天进步一点点,理想终会被实现. 前言 设计模式,可能很多人都是看到代码知道怎么回事,但是离开代码再让其说出来,估计就有点含糊其词了,包括我自己在内.Android中其实用到的设计模式也 ...

最新文章

  1. 2022-2028年中国PE膜产业竞争现状及发展前景分析报告
  2. 《强化学习周刊》第6期:强化学习应用之推荐系统
  3. ASP.NET中验证控件的使用
  4. 全球及中国制糖行业销售规模与运营态势研究报告2022版
  5. 没看完这11 条,别说你精通 Python 装饰器
  6. SQL Server数据归档的解决方案
  7. Hibernate注解使用以及Spring整合
  8. 微服务统计,分析,图表,监控, 分布式追踪一体化的 HttpReports 在 .Net Core 的应用...
  9. 关于 NIO 你不得不知道的一些“地雷”
  10. [学习笔记]0/1分数规划
  11. html 嵌入编辑excel 开源_网页中嵌入Excel控件
  12. 树莓派使用433Mhz射频无线收发
  13. java opts配置_JAVA_OPTS设置参数
  14. 印刷纸张尺寸,纸张种类规格
  15. SUMO交通仿真-核心概念和基础知识速览
  16. [20160831]关于数据块Checksum.txt
  17. 【原创】带下划线单选菜单栏-标签Tab
  18. HTML5 全局属性
  19. 秃头警告之——使用mondo rescue备份linux系统ISO镜像的踩坑历程
  20. 作业:自行录制轻音、浊音、爆破音并使用Audacity分析其时域和频域的特性

热门文章

  1. tny278功能参数_tny27-280中文资料.pdf
  2. 海森矩阵和半正定矩阵
  3. pageoffice在线编辑时向保存方法传递参数
  4. Android 7.0以上版本 HTTPS 抓包解决方法
  5. 随机森林算法原理简要总结
  6. matlab 坐标轴根号,Matlab中根号表示怎么操作?根号表示教程分享
  7. PID算法与PID自整定算法
  8. python tkinter canvas
  9. 在线生成抖音风格的文字
  10. 常见的五种前端页面布局(table布局、float布局、absolute布局、flex布局、grid布局)