责任链模式及运用——事件分发,OKhttp拦截器

详细解析责任链设计模式和相关运用,比如事件分发机制,okHttp的拦截器运用。

 

别的先不说,上图,个人觉的这张简单的图可以说清楚责任链模式的相关,即对象A产生了一个问题,它交给对象B去处理,而B其实只是一个抽象Process 定义了相关处理问题抽象方法, 不具备真正的处理能力,而其子类可以实际真正的去处理,可能B有多个子类B1,B2.,B3.... 到底这些哪个可以处理其实不知道,于是就先交给B1(处理数学问题),B1无法解决就给B2(处理化学问题),依次类推,这样就形成了一个链条,而且B1,B2,B3 各司其职都有自己擅长的领域,都只能处理各自领域的问题,所以就相当于各自必须处理各自领域的问题,相对于有了责任,所以,整体看来就是形成了一个责任链条,于是就叫了责任链模式。

 

 

那我们用伪代码描述就可以

public abstract class Process { 
    
    private boolean isConsume; 
    
    public void handleRequest() {  
        if (isConsume) {    
            // 如果当前节点可以处理,直接处理     
            doSomething();  
        } else {     
            // 如果当前节点不能处理,并且有下个节点,交由下个节点处理     
            if (null != nextProcess) {     
                nextProcess.handleRequest();    
            } 
        } 
    } 

------------------------------------------------------------------------------------
    // 下一个责任节点
    private Process nextProcess; 
    
    public void setNextProcess(Process nextProcess) {  
        this.nextProcess = nextProcess; 
    }  
    
    abstract protected void doSomething();

}

此时,我们归纳一下责任链模式的两点:

  1.  处理机制(各个处理者处理事件规则)
  2. 如何把事件向下传(事件在处理者之间如何流转)

处理机制(各个处理者处理事件规则)

 public void handleRequest() {  
        if (isConsume) {    
            // 如果当前节点可以处理,直接处理     
            doSomething();  
        } else {     
            // 如果当前节点不能处理,并且有下个节点,交由下个节点处理     
            if (null != nextProcess) {     
                nextProcess.handleRequest();    
            } 
        } 
    } 

显然,上面代码就是处理规则。看到伪代码分割线以上的部分其实很容易想到Android事件分发的伪代码,其实二者是一样的,所以我们说Android的事件传递机制也是责任链设计模式。

public boolean dispatchTouchEvent(MotionEvent ev) {

        boolean consume = false;

        if(onInterceptTouchEvent(ev)){
            consume = onTouchEvent(ev);
        }else {
            consume = child.dispatchTouchEvent(ev);
        }

        return consume;
 
}

对比一看,确实是一样的。就是第一个处理者接到请求后,进行判断自己是否能够处理,不能则向下一个处理者传递,否则自己处理掉。

另外,还要说明一下,责任链模式核心就是同一个请求可以给多个具有不同处理能力的处理者,每个处理者只能处理自己能力范围之内的请求,满足这个描述其实都可以看作为责任链,这样就会出现很多的变体,比如说处理者是否有序、请求被处理后则流程结束还是继续被其他的处理者处理,再不同的场景可以用不同的方式,灵活看待。

如何把事件向下传(事件在处理者之间如何流转)

对于这点,上面也提到了——就是持有下一个处理器的引用,并去调用下一个处理者的处理方法


    else {     
            // 如果当前节点不能处理,并且有下个节点,交由下个节点处理     
            if (null != nextProcess) {     
                nextProcess.handleRequest();    
            } 
        } 


   // 下一个责任节点
    private Process nextProcess; 
    
    public void setNextProcess(Process nextProcess) {  
        this.nextProcess = nextProcess; 
    }  
    
   

这是一种思路,我们看看okhttp的拦截器的责任链怎么设计的?对于如何使用,大家都知道:

 private MyOkhttpClient(){
        //加入过滤器 及公共参数
        //实现日志打印
        okHttpClientBuilder = new OkHttpClient.Builder()
                        //.addInterceptor(new RequestInterceptor(httpClientType & 2))
                        .connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
                        .writeTimeout(DEFAULT_WRITE_TIMEOUT, TimeUnit.MILLISECONDS)
                        .readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.MILLISECONDS)
                                     .addInterceptor(new RequestInterceptor(1));
        if (HttpUtil.HTTP_LOG){
            HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new MyLogging());
            httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            okHttpClientBuilder.addInterceptor(httpLoggingInterceptor);

            //okHttpClientBuilder.addInterceptor(new LoggingInterceptor());
        }
    }

显然,使用很简单,不知道大家有没有想过为什么对于OkhttpClinet.builder 添加这样一个Interceptor就可以实现日志打印功能呢?对此,我们从使用okhttp 开始研究:

OkHttpClient client = new OkHttpClient();//创建OkHttpClient对象
            Request request = new Request.Builder()
                    .url("http://www.baidu.com")//请求接口。如果需要传参拼接到接口后面。
                    .build();//创建Request 对象
            Response response = null;
            response = client.newCall(request).execute();

ok, 我们看到最后一行代码 client.newCall(request).execute();  这里点击execute方法,

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }

关注 getResponseWithInterceptorChain(); 

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    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));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

这里就对okhttp里面内置的拦截器(处理器)进行添加,并构造了一个链条chain,然后调用chain.proceed(request);再点击该方法会发现到了一个  接口定义类

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

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    /**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */
    @Nullable Connection connection();

    Call call();

.......

}

在继续跟Reponse proceed(Request request) 方法,到了 RealIntercepteorChain ,见明知义,这个类才是真正的处理者,定义处理请求的规则。继续跟进:

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to 
   chain.proceed().

   .......

    // 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;
  }

看到这个方法,我们知道这就是真正我们想要找的方法了,这个类包含之前的拦截器链条 interceptors ,第一行的代码含义是拦截器数量检查,第二行call ++ 意思是当前拦截器的计数器,通过计数器的方式替代 interceptors  for循环调用拦截器的proceed() 方法,当你看到 

Interceptor interceptor = interceptors.get(index);

这句代码的时候你就知道这个计数器的作用了。其实我之前一直再找for循环,但一直都没有找到,欸。固化思维!好了到此就分析完okhttp拦截器的拦截的原理了(后续补一个图....)

对于okhttp拦截器的其他方面,看官网 ,有各种拦截器的详细介绍。

至此,我们还可以继续深挖Okhttp的责任链模式,比如对于请求公共参数的添加是怎么实现的? 添加多个拦截器会怎么样,拦截器之间会有影响吗?这些都是可以继续研究,研究的越多,你理解的就越深。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章