(Android) OkHttp3.10 源码学习笔记 2 异步请求分析

接上篇同步请求分析,首先我们先看简单的异步请求的使用方法。

1. 异步请求使用

OkHttpClient client = new OkHttpClient();
       Request request = new Request.Builder()
               .url("http://baidu.com")
               .build();
       Call call = client.newCall(request);
       call.enqueue(new Callback() {
           @Override
           public void onFailure(Call call, IOException e) {
               
           }

           @Override
           public void onResponse(Call call, Response response) throws IOException {

           }
       });

从上面的代码来看,和同步请求不同的部分就是最后是执行的call的enqueue方法,传入的是一个callback对象,用于回调请求的结果。

2. enqueue方法分析

我们还是进入Call的实现类RealCall来看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));
  }

大部分代码与同步请求的无差别,主要看最后一行,它用我们传入的callback对象封装了一个AsyncCall对象,并传入dispatcher.

AsyncCall其实就是个Runnable,AsyncCall类中会重写NamedRunnable中的excute方法。

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

继续看dispatcher的enqueue方法。主要逻辑也比较简单:对runningAsyncCalls队列的size和最大请求数(5)进行判断,判断通过就将call入队,并调度线程池执行。否则就将请求加入readyAsyncCalls队列,在合适的时机再放入runningAsyncCalls队列

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

3.线程池的创建

同样在Dispatcher类中,我们看一下线程池的创建过程

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

corePoolSize:0 设定为0可以在一段时间空闲后销毁所有线程

最大线程数量:integer的最大值(有同学可能会有疑问,最大线程数量是最大值,创建线程是否会对性能有严重影响?其实是不会的,Dispatcher类中已经对最大请求数做了限制 maxRequests = 64;)

SynchronousQueue:是一种没有容量的阻塞队列,每put一个元素后必须要等待一个take,比较适合做传递性设计


4.AsyncCall分析

上面提到了AsyncCall,他是继承自NamedRunnable,NamedRunnable又实现了Runnable接口,在run方法中,执行了execute方法,这个是一个抽象方法,会在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的execute方法

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

首先,这边使用拦截器链获取一个response,接着判断拦截器执行是否取消,取消则回调oFailure方法,否则得到response。同样要注意,这里回调的fail和response都是在子线程中,因为整个过程在独立线程中执行,并没有做线程切换。

再看finally的设计,调用了dispatcher的finished方法,这个方法在同步请求分析时提到过,移除了执行完毕的call,但是这里有个标志位和同步请求的不同

void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }
 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();
    }
  }

这里promoteCalls为ture,会调用promoteCalls方法,promoteCalls方法里会对Ready和running这两个队列的call进行调整,然后重新计算正在执行的线程数量,详细的会在Dispatcher中进行分析。

到这里,异步请求的细节基本分析完毕,下一节,我们一起来分析核心的Dispatcher


猜你喜欢

转载自blog.csdn.net/zhouy1989/article/details/80538378