美文网首页
OkHttp 源码分析系列(二)- Dispatcher

OkHttp 源码分析系列(二)- Dispatcher

作者: 琼珶和予 | 来源:发表于2018-10-03 19:43 被阅读0次

  如果要说OkHttp中的大头,Dispatcher肯定是占据一席之地的。在OkHttp中,Dispatcher负责任务的分发和调度。今天来重点的说说它。
  其实这个类非常的简单,大家不要将它想的太难了。

1. 同步请求的Dispatcher

  同步请求是通过Callexecute方法来执行的,虽然我们在分析同步请求时,已经看过这个方法了,但是在这里我们还是再来看一遍,这一次我们只关心Dispatcher相关的操作:

  @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方法之前,我们首先Dispatcherexecuted方法,我们来看看在这个方法是怎么进行操作.

synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

  同步请求的操作非常简单,直接将当前这个Call添加到runningSyncCalls队列,就没有做其他的事情了。
  同时在execute方法的最后,还调用了Dispatcherfinished方法。我猜,肯定是将当前的Call对象从队列中移除。
  同步请求的Dispatcher过程就是这么简单。接下来,我们来看看异步请求的Dispatcher过程。

2. 异步请求的Dispatcher

  异步请求是通过Callenqueue方法来执行的,我们来看看:

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

  异步执行的核心在enqueue方法最后一行。我们来看看Dispatcherenqueue方法:

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

  这个方法在上一篇文章中已经分析过了,这这里就不在做过多的解释,这里我们需要注意的一点是:当不满足if条件时,会将当前的Call对象放在一个等待队列中,但是什么时候将当前这个Call从等待队列移除呢?这个就是Dispatcher的职责了。关于这个问题,我们需要从AsyncCallexecute方法,我们来看看:

    @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 {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

  我们重点关注如下代码:

client.dispatcher().finished(this);

  如上的代码最终会执行到Dispatcherfinished方法中去:

  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

  而在调用finished时,promoteCalls参数我们传入的是true,所以会调用promoteCalls方法,最后,我们可以这个方法里面来找到答案:

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

  从上面的代码中,我们可以看出来,当当前的执行队列不是满的的话,就会从等待队列去取出相应的Call放入执行队列中去,并且提交到线程池去执行。
  这就是异步请求的任务调度过程,是不是也是非常的简单?

3.总结

  同步请求的调度过程是非常简单的,这里就不对同步做总结,而对异步请求做一个简单的总结。
  1. 异步请求通过调用Callenqueue方法将一个Call对象放入到一个队列当中,如果当前的执行队列没有满的话,就放在执行队列中,立即执行;如果满了的话,就放在等待队列中去。
  2.异步请求,当一个任务执行完毕之后,会调用Dispatcherfinished方法。在finished方法里面,会调用promoteCalls方法会重新调整队列。

相关文章

网友评论

      本文标题:OkHttp 源码分析系列(二)- Dispatcher

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