美文网首页
Okhttp之责任链

Okhttp之责任链

作者: dreamruner | 来源:发表于2018-07-18 19:10 被阅读59次

    Okhttp之责任链

    Okhttp简介

    okhttp是一个第三方类库,用于android中请求网络。
    这是一个开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献(该公司还贡献了Picasso和LeakCanary)

    官网网址:OKHttp官网

    基本用法

    OkHttpClient client = new OkHttpClient();
    
    String run(String url) throws IOException {
      Request request = new Request.Builder()
          .url(url)
          .build();
    
      Response response = client.newCall(request).execute();
      return response.body().string();
    }
    

    这里大概分为四步

    1. 构建OkHttpClient对象,配置公共的网络请求设置,如超时时间,拦截器等
    2. 构建Request对象,主要有url,method,RequestBody
    3. 生成call对象
    4. 获得Response对象

    OkhttpClient,Request的构建都使用了建造者模式,这里不做详细介绍.

    接下来通过这个例子分析整个流程:

    #OkhttpClient.java
    /**
       * Prepares the {@code request} to be executed at some point in the future.
       */
      @Override public Call newCall(Request request) {
        return RealCall.newRealCall(this, request, false /* for web socket */);
      }
    
    

    OkhttpClient的newCall方法其实是调用RealCall 的一个静态方法,跟进去看一下

    #RealCall.java
    
    final class RealCall implements Call {
    
    .....
    static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        // Safely publish the Call instance to the EventListener.
        RealCall call = new RealCall(client, originalRequest, forWebSocket);
        call.eventListener = client.eventListenerFactory().create(call);
        return call;
      }
    
    }
    

    这里逻辑很简单,就是new了一个RealCall的实例,而RealCall 则是Call 接口的一个实现类,接下里看一下Call接口都定义了哪些方法:

    /**
     * A call is a request that has been prepared for execution. A call can be canceled. As this   object
     * represents a single request/response pair (stream), it cannot be executed twice.
     */
    public interface Call extends Cloneable {
      /** Returns the original request that initiated this call. */
      Request request();
      
      Response execute() throws IOException;
        
      void enqueue(Callback responseCallback);
    
      /** Cancels the request, if possible. Requests that are already complete cannot be canceled. */
      void cancel();
    
      /**
       * Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
       * #enqueue(Callback) enqueued}. It is an error to execute a call more than once.
       */
      boolean isExecuted();
    
      boolean isCanceled();
    
      /**
       * Create a new, identical call to this one which can be enqueued or executed even if this call
       * has already been.
       */
      Call clone();
    
      interface Factory {
        Call newCall(Request request);
      }
    }
    

    对于Call接口的定义,官方给出了解释:

    A call is a request that has been prepared for execution. A call can be canceled. As this object

    represents a single request/response pair (stream), it cannot be executed twice.

    翻译: 一个Call是为执行而准备的请求,它能够被取消,这个对象代表一个请求/响应对(流),不能够被执行两次.

    大致就是Call相当一个真正用于网络请求的任务,它可以被取消,它里面的请求和响应是一一对应的,不能够被执行两次,大概可能导致状态异常吧(瞎猜的),所以每个请求需要新建一个Call的实例,不能够复用之前的.

    Call中也包含了关键的用于同步请求的execute()方法,用于异步请求的enqueue()方法.

    接下来看一下RealCall对于execute()的实现:

    #RealCall.java
    
    @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);
        }
      }
    

    首先判断executed是否为true,如果是true则抛出异常,同时也保证了Call只能够被执行一次.

    首先分析client.dispatcher().executed(this);

    #Dispatcher.java
    
    public final class Dispatcher {
      private int maxRequests = 64;
      private int maxRequestsPerHost = 5;
      private @Nullable Runnable idleCallback;
    
      /** Executes calls. Created lazily. */
      private @Nullable ExecutorService executorService;
    
      /** Ready async calls in the order they'll be run. */
      private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
    
      /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
      private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    
      /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
      private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
        
        
      /** Used by {@code Call#execute} to signal it is in-flight. */
      synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
      }
        
    }
    
    

    可以看到Dispatcher 其实就是一个任务调度器 ,它里面维护了线程池,和三个任务队列(双向队列)

    • readyAsyncCalls : 等待异步请求的队列
    • runningAsyncCalls: 运行的异步请求队列
    • runningSyncCalls : 运行的同步请求队列

    executed()方法则是将当前同步请求任务加入到同步请求队列中

    这里先提一下Interceptor ,也就是我们所说的拦截器.那它到底是啥呢?

    #Interceptor.java
    
    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();
    
    int connectTimeoutMillis();
    
    Chain withConnectTimeout(int timeout, TimeUnit unit);
    
    int readTimeoutMillis();
    
    Chain withReadTimeout(int timeout, TimeUnit unit);
    
    int writeTimeoutMillis();
    
    Chain withWriteTimeout(int timeout, TimeUnit unit);
    }
    
    }
    

    没错,它就是一个接口,而且只有一个intercept方法,它接收一个实现了Chain 接口的对象,并返回一个Response对象.

    接下来继续分析Okhttp如何进行网络请求,并获取响应对象.

    Response result = getResponseWithInterceptorChain();
    

    getResponseWithInterceptorChain 才是真正执行网络请求并获得Response对象,看一下具体实现:

    #RealCall.java
    
    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);
      }
    

    它首先创建了一个空的List,并把我们用户自定义的拦截器添加进去,然后添加Okhttp 预先定义的拦截器,主要有以下几个:

    • RetryAndFollowUpInterceptor : 负责失败重试和重定向
    • BridgeInterceptor : 负责处理http 通用请求头和返回头,Cookie
    • CacheInterceptor : 负责处理缓存
    • ConnectInterceptor : 开启和服务器的连接
    • networkInterceptors : 用户定义的网络拦截器,权限更高,更接近底层,一般不需要关注
    • CallServerInterceptor : 实现网络请求

    这里只是大概介绍每个拦截器的职责,不作详细解析,有兴趣可以自己查看相关源代码.

    Okhttp实际上在这里把这些拦截器进行了排序,任何一个顺序改变,都可能导致无法正常工作.

    接下来看一下okhttp是如何将这些拦截器串联在一起工作的:

    在处理完所有拦截器之后,它创建了一个RealInterceptorChain 对象,并把拦截器列表作为参数传了进去,然后调用了proceed 方法.

    #RealInterceptorChain.java
    
      @Override public Response proceed(Request request) throws IOException {
        return proceed(request, streamAllocation, httpCodec, connection);
      }
    
      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().
        if (this.httpCodec != null && calls > 1) {
          throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
              + " must call proceed() exactly once");
        }
    
        // 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);
    
        // Confirm that the next interceptor made its required call to chain.proceed().
        if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
          throw new IllegalStateException("network interceptor " + interceptor
              + " must call proceed() exactly once");
        }
    
        // Confirm that the intercepted response isn't null.
        if (response == null) {
          throw new NullPointerException("interceptor " + interceptor + " returned null");
        }
    
        if (response.body() == null) {
          throw new IllegalStateException(
              "interceptor " + interceptor + " returned a response with no body");
        }
    
        return response;
      }
    

    这里除去参数校验可以分为3步;

    1. new一个RealInterceptorChain对象,并把当前的RealInterceptorChain对象的成员原封不动当作参数传入,唯一不同的就是把index+1之后传入。
    2. 并从拦截器列表中获取第index个位置上的拦截器对象。index最初传入的是0,之后每当proceed方法调用一次,也就是new一个RealInterceptorChain对象时,会对index+1,此时就会拿到拦截器列表的下一个对象,其实就是在进行遍历。
    3. 调用上一步拿到的拦截器的intercept方法,每个拦截器内部可以决定中断或延续整个责任链,中断则返回Response对象,延续则调用chain.procced()方法进入到下一个Interceptor.

    至此整个责任链模式分析完毕。

    总结

    其实就是利用的迭代加递归的思想,在每个拦截器的内部调用chain.proceed方法,即调用下一个Interceptorintercept方法,而每个拦截器又可以自己决定中断或延续整个调用过程,但如果需要发起网络请求,则必须走到最后一个拦截器由CallServerInterceptor发起请求并返回Response,并依次返回。
    每个拦截器都有自己的职责,可以处理从上游传过来的请求对象,也可以处理从下游返回的请求对象,设计相当优雅.

    最后附上整个过程的流程图(来自网络):

    相关文章

      网友评论

          本文标题:Okhttp之责任链

          本文链接:https://www.haomeiwen.com/subject/ccsapftx.html