(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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章