OkHttp分发器与拦截器

OkHttp的分发器与拦截器详解

上文我们有提到过的okHttpClient中比较重要的几个属性

Dispatcher dispatcher;    //分发器
final List<Interceptor> interceptors = new ArrayList<>();   //application级拦截器
final List<Interceptor> networkInterceptors = new ArrayList<>();   //低一级的拦截器
分发器(Dispatcher)

我们先来看一下call.execute()这个方法中到底发生了什么可以得到一个response对象。

//RealCall.java
@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }

我们可以看到try里面的第一句就是client.dispatcher().executed(this)。client.dispatcher()就是获取该okHttpClient对象的一个对应的分发器,儿这个分发器其实在okHttpClient的构造方法中已经初始化好了,我们点进去看一下这个executed(RealCall call)。

/** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }
  //runningSyncCalls 是一个Deque<RealCall>对象

runningSyncCalls是一个双向队列,也就相当于把这个要进行网络访问的call加入到一个双队列中。
接下来的getResponseWithInterceptorChain()的方法就会在分析拦截器的时候再进行分析,怎么就可以得到一个Response对象。
下面我们再来看看call.enqueue()发生了什么

@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

我们跟进去看一下diapatcher.enqueue方法

synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

因为是异步请求,所以在同一时间发出的最大请求量,与同一主机的最大访问量都有一个最大值。if的判断条件中maxRequests是最大请求量,默认是60,maxRequestsPerHost是同一主机访问最大量,默认是5。如果满足这个条件,我们就像要执行任务的双向队列中加入这个call对象,然后调用executorService().execute(call)方法。如果超过了呢,就往readyAsyncCalls中添加这个对象,readyAsyncCalls也是一个双向队列,不过它与runningAsyncCalls的区别是它里面包含的都是准备执行的call,不是正在执行的call,当我们一个call执行完的时候,会再去判断readyAsyncCalls是否有call,有就取一个最早加入的call加入runningAsyncCalls,并且调用executorService().execute(call)方法,那么executorSercvie()方法

public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

就是获取一个线程池,看起来这个线程池无限大,其实因为我们设置了最大访问量默认六十的话,这个线程池工作线程数量是不会太大的。
那么我们看一下AsyncCall中execute(call)方法发生了什么

@Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

try里面的第一句就是调用这个方法getResponseWithInterceptorChain(),然后后面就是比较常规的回调onFailure()和onResponse()方法,看来很关键的一个问题就是这个getResponseWithInterceptorChain()发生了什么,我们点进去看一下。

拦截器
private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(
        retryAndFollowUpInterceptor.isForWebSocket()));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }
}

看起来比较简单,首先加入在构造okHttpClient可以定义的interceptors,前面也说了这个application级别的拦截器,它最先加入,接着是系统自带的一些拦截器。然后加入自定义的networkInterceptors拦截器,最后加入一个CallServerInterceptor拦截器。再构建一个Interceptor.Chain对象,这个对象是一个拦截器链,就是相当于我们刚传进去的拦截器的一个链表一样,注意这里传进去一个参数index 为0,然后调用chain.proceed方法,我们跟进去

@Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpStream, connection);
  }

再跟这个proceed方法

public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
      Connection 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.httpStream != null && !sameConnection(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.httpStream != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // Call the next interceptor in the chain.
    //注意这里,又去构建一个index = index + 1 的拦截器链
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpStream, connection, index + 1, request);
        //获取拦截器,下标为0开始
    Interceptor interceptor = interceptors.get(index);
    // 这个方法很重要,跟进去看看
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    if (httpStream != 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");
    }

    return response;
  }

我们再看看interceptor.intercept(next)方法。
这个方法intercept 各个拦截器有各个的实现方法,我们自定义拦截器也是要去实现这个方法,那我们看一下最先加进去的retryAndFollowUpInterceptor是怎么实现的

@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();

    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()));

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response = null;
      boolean releaseConnection = true;
      try {
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        if (!recover(e.getLastConnectException(), true, request)) throw e.getLastConnectException();
        releaseConnection = false;
        continue;
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        if (!recover(e, false, request)) throw e;
        releaseConnection = false;
        continue;
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          streamAllocation.streamFailed(null);
          streamAllocation.release();
        }
      }

内容比较多,其实可以这么去看

 处理request
 response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
 处理response

这样就清晰很多了,一个很标准的递归,就是相当于假设我有三个拦截器,一号拦截器最先加进去,一号先处理request,处理完做为参数传给二号,二号也处理request再传给三号,三号发送request得到最开始的response返回给2号,2号处理一下response再返回给1号,然后最后就成了我们得到的response对象,那么按照这么说,最下面的拦截器应该就不会调用proceed方法了把,的确如此,最下面的拦截器是CallServerInterceptor,我们点进去看一下它的intercept方法。

 @Override public Response intercept(Chain chain) throws IOException {
    HttpStream httpStream = ((RealInterceptorChain) chain).httpStream();
    StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation();
    Request request = chain.request();

    long sentRequestMillis = System.currentTimeMillis();
    httpStream.writeRequestHeaders(request);

    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      Sink requestBodyOut = httpStream.createRequestBody(request, request.body().contentLength());
      BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
      request.body().writeTo(bufferedRequestBody);
      bufferedRequestBody.close();
    }

    httpStream.finishRequest();

    Response response = httpStream.readResponseHeaders()
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    if (!forWebSocket || response.code() != 101) {
      response = response.newBuilder()
          .body(httpStream.openResponseBody(response))
          .build();
    }

    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      streamAllocation.noNewStreams();
    }

    int code = response.code();
    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;
  }

可以看到的确没有再调用那个方法,它其实已经在和服务端进行数据交流了,得到原始的response。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值