OKHttp原理讲解之责任链模式及扩展

一、前言:

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更新到缓存中。

猜你喜欢

转载自blog.csdn.net/AA5279AA/article/details/123557335
今日推荐