个人笔记---OKhttp源码跟踪之异步

前言

关于okhttp的介绍和同步请求,请看上一篇文章,个人笔记---Okhttp源码跟踪之同步,这里就不在赘述了,直接进入正题。

异步请求

    /**
     * 异步请求
     */
    public void asyncCall(Callback callback) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("https://gank.io/api/today")
                .build();
        client.newCall(request).enqueue(callback);
    }

异步请求时多了一个callback回调,请求结果会在这个回调中返回。
前面创建call请求实例步骤和同步是一样的,关键步骤就是异步调用的是enqueue方法,跟踪一下这个方法。

@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));
  }

异步也需要判断当前call请求是否已经执行过,逻辑和同步也是一样的。然后又到了任务调度器的enqueue方法,并且传入了一个AsyncCall实例,AsyncCallRealCall的内部类, 实现了Runnable接口,后续请求的时候会被放入线程池中,继续跟踪dispatcherenqueue方法

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

首先会判断当前正在运行的异步队列的大小是否小于了64(默认支持最大并发送为64),单个host请求数是否小于5,满足两个条件,将请求加入runningAsyncCalls队列,并且将call请求放入线程池执行,如果任一条件不满足,则将请求放入readyAsyncCalls等待队列,等待队列的逻辑会放在最后分析

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

如果线程池为空,则创建一个核心线程为0,最大线程为Integer.MAX_VALUE, 空闲线程存在的最大时间为60s的线程池,线程池创建完毕以后就开始执行AsyncCall请求

public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

AsyncCall这个内部类在异步请求中还是比较重要的,它继承了一个叫NamedRunnable的抽象类,当线程运行时会调用execute这个抽象方法,因为AsyncCallRealCall的内部类,所以这个抽象方法的具体实现是在RealCall里面,继续跟踪

@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);
      }
    }
  }

通过一系列的拦截器去获取网络请求结果,过程和同步是同样的,关于拦截器的过程我会专门开一篇文章记录,这里暂时不详细说。
如果请求被取消则回调onFailure接口,未被取消则回调onResponse接口,我们可以看到回调的两个方法并没有切换线程,还是在子线程中,不能操作UI,当前请求执行完毕,调用dispatcherfinished方法

private  void finished(Deque 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();
    }
  }

将执行完毕的请求从runningAsyncCalls队列中移除,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 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.
    }
  }

如果此时请求数超过maxRequest数量,则直接return;若readyAsyncCalls等待队列中没有请求,则直接return;遍历等待队列,如果此时单个host请求数量小于5,将此请求从等待队列移除并加入到runningAsyncCalls队列中,将请求放入线程池中去执行,执行结束也会调用promoteCalls这个方法,直到readyAsyncCalls队列为空,这样就完成了所有请求。

至此,整个异步请求的过程也分析完成,因为能力有限,分析的比较粗略,只是对整个请求的流程梳理一下,后续会再深究一下其中的细节。

因水平有限,如有错误请指正,不胜感激!

你可能感兴趣的:(个人笔记---OKhttp源码跟踪之异步)