美文网首页
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