OkHttp 源碼簡要分析二 (Dispatcher 請求機制詳解)

get 方式網絡請求

    private void okhttp() {
        OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
                .build();

        Request request = new Request.Builder()
                .url("http://publicobject.com/helloworld.txt")
                .build();
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback()
        {

            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String string = response.body().string();
                Log.e("onResponse", string);
            }
        });
    }

這個是一個簡單的網絡請求,Call call = mOkHttpClient.newCall(request) 這行代碼,返回對象是 RealCall ,它是 Call 的實現類

  @Override 
  public Call newCall(Request request) {
    return new RealCall(this, request);
  }

下面是 RealCall 的簡化代碼,看一下

final class RealCall implements Call {
    private final OkHttpClient client;
    private final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
    private boolean executed;
    Request originalRequest;

    protected RealCall(OkHttpClient client, Request originalRequest) {
        this.client = client;
        this.originalRequest = originalRequest;
        this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);
    }

    @Override public Response execute() throws IOException {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        try {
            client.dispatcher().executed(this);
            Response result = getResponseWithInterceptorChain();
            if (result == null) throw new IOException("Canceled");
            return result;
        } finally {
            client.dispatcher().finished(this);
        }
    }

    @Override public void enqueue(Callback responseCallback) {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }

    ...

    final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;

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

        @Override protected void execute() {
            ...
        }
    }
}


繼續看例子中的請求 call.enqueue() 這個方法,對應的是 RealCall 中的方法,注意,此時 client.dispatcher().enqueue(new AsyncCall(responseCallback)),這個方法注意兩點,AsyncCall 和 Dispatcher, AsyncCall 是個包裝類,NamedRunnable 實現了 Runnable 接口,抽象出 execute() 方法;AsyncCall 是個線程調度管理的類,這裏是異步請求

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

首先會比較正在請求的網絡個數有沒有超過 maxRequests 默認 64 的值,runningCallsForHost(call) 這個方法是此次請求幾口庫的域名與正在請求的接口中使用相同域名接口的個數,如果它小於 maxRequestsPerHost 默認 5 的值,則可以請求,使用 executorService() 創建線程池來執行 Runnable,這裏是創建了一個不限制線程個數的線程池;如果不滿足if判斷條件,則把此次請求添加到等待隊列中。如果線程池執行了 Runnable ,重新回到 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) {
                    Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
                } else {
                    responseCallback.onFailure(RealCall.this, e);
                }
            } finally {
                client.dispatcher().finished(this);
            }
        }

Response response = getResponseWithInterceptorChain() 這個是獲取服務端返回的報文,這個方法中步驟比較多,使用了責任鏈模式,這個下一篇再講,這裏知道是返回報文即可。這裏先判斷接口請求是否被取消,如果取消,回調 onFailure() 回調,否則是 onResponse() 回調;如果在 getResponseWithInterceptorChain() 過程中出現了異常,則會執行 catch 中的回調,也是 onFailure() 回調;由於這裏使用了 try...catch 方法,所以最終會執行 finally 中的代碼,這裏掉用了 Dispatcher 的 finish() 方法

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

這個方法中,現實使用 calls.remove(call) ,這個是把當前對象從集合中移除,如果失敗,拋異常;成功的話,執行 promoteCalls() 方法;然後重新獲取正在請求接口的個數,如果沒有了正在請求的接口,並且 idleCallback 回調不爲空,則執行 idleCallback 回調。現在看看 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.
    }
  }


首先判斷是否達到了最大個數以及是否還存在等待請求的接口,如果滿足條件,則遍歷等待請求的集合,從前往後獲取,只要當前請求url的域名沒有超過5個的限制,則從等待集合中移除該請求,添加到請求集合中,並使用線程池開啓線程來執行該請求,到此就一條新的請求開始了;由於是遍歷,則會牽涉到循環,一旦正在請求的接口達到了64的上限,則跳出for循環。

同步請求有點特殊,需要我們自己開啓線程,這個會把當前請求添加到正在請求的集合中,會影響異步請求 64 和 5 的限制,但對同步本身沒有限制,因爲它沒有這兩個值的限制。

  @Override 
  public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }

同步執行後,最終會執行 finally 方法,調用 Dispatcher 的 finished() 方法,檢查是否還有請求需要執行。

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