個人筆記---OKhttp源碼跟蹤之異步

前言

關(guān)于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回調(diào),請求結(jié)果會在這個回調(diào)中返回。
前面創(chuàng)建call請求實例步驟和同步是一樣的,關(guān)鍵步驟就是異步調(diào)用的是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));
  }

異步也需要判斷當(dāng)前call請求是否已經(jīng)執(zhí)行過,邏輯和同步也是一樣的。然后又到了任務(wù)調(diào)度器的enqueue方法,并且傳入了一個AsyncCall實例,AsyncCallRealCall的內(nèi)部類, 實現(xiàn)了Runnable接口,后續(xù)請求的時候會被放入線程池中,繼續(xù)跟蹤dispatcherenqueue方法

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

首先會判斷當(dāng)前正在運行的異步隊列的大小是否小于了64(默認(rèn)支持最大并發(fā)送為64),單個host請求數(shù)是否小于5,滿足兩個條件,將請求加入runningAsyncCalls隊列,并且將call請求放入線程池執(zhí)行,如果任一條件不滿足,則將請求放入readyAsyncCalls等待隊列,等待隊列的邏輯會放在最后分析

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

如果線程池為空,則創(chuàng)建一個核心線程為0,最大線程為Integer.MAX_VALUE, 空閑線程存在的最大時間為60s的線程池,線程池創(chuàng)建完畢以后就開始執(zhí)行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這個內(nèi)部類在異步請求中還是比較重要的,它繼承了一個叫NamedRunnable的抽象類,當(dāng)線程運行時會調(diào)用execute這個抽象方法,因為AsyncCallRealCall的內(nèi)部類,所以這個抽象方法的具體實現(xiàn)是在RealCall里面,繼續(xù)跟蹤

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

通過一系列的攔截器去獲取網(wǎng)絡(luò)請求結(jié)果,過程和同步是同樣的,關(guān)于攔截器的過程我會專門開一篇文章記錄,這里暫時不詳細(xì)說。
如果請求被取消則回調(diào)onFailure接口,未被取消則回調(diào)onResponse接口,我們可以看到回調(diào)的兩個方法并沒有切換線程,還是在子線程中,不能操作UI,當(dāng)前請求執(zhí)行完畢,調(diào)用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();
    }
  }

將執(zhí)行完畢的請求從runningAsyncCalls隊列中移除,promoteCalls為異步標(biāo)志位,此時為true,會執(zhí)行到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.
    }
  }

如果此時請求數(shù)超過maxRequest數(shù)量,則直接return;若readyAsyncCalls等待隊列中沒有請求,則直接return;遍歷等待隊列,如果此時單個host請求數(shù)量小于5,將此請求從等待隊列移除并加入到runningAsyncCalls隊列中,將請求放入線程池中去執(zhí)行,執(zhí)行結(jié)束也會調(diào)用promoteCalls這個方法,直到readyAsyncCalls隊列為空,這樣就完成了所有請求。

至此,整個異步請求的過程也分析完成,因為能力有限,分析的比較粗略,只是對整個請求的流程梳理一下,后續(xù)會再深究一下其中的細(xì)節(jié)。

因水平有限,如有錯誤請指正,不勝感激!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容