OkHttp源碼探索

之前一直在使用volley作爲網絡請求模塊,但發現volley已經過時(源碼還是很值得反覆品味的),現在基本是RetrofitOkHttp的天下了,於是把OkHttp拿來學習一下。
首先,看一個OkHttp的簡單示例:

        Request.Builder builder = new Request.Builder().url(url).get();
        Request okRequest = builder.build();
        Call call = mOkHttpClient.newCall(okRequest);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

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

                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        listener.onFetchFinished(response.body().string());
                    }
                });
            }
        });

該類框架都比較類似,創建一個請求及回調,然後加入請求隊列,後面就由框架來進行任務調度,和結果回調。具體的細節,後面使用時再慢慢看,現在先探索一下OkHttp的主路線。

首先, 把請求加入到任務隊列:

//okhttp3.RealCall.java

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

此方法就爲避免單個請求多次添加,

// Dispatcher.java

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

  /** Returns the number of running calls that share a host with {@code call}. */
  private int runningCallsForHost(AsyncCall call) {
    int result = 0;
    for (AsyncCall c : runningAsyncCalls) {
      if (c.host().equals(call.host())) result++;
    }
    return result;
  }

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

這個方法比較有意思,很明顯有兩個任務隊列,一個是正在執行的隊列(runningAsyncCalls)和一個等待隊列(readyAsyncCalls);不僅限制了最大併發請求的數量,還對同一個域名的併發請求做了限制,當然單個應用可能請求的域名都是一樣的,所以這個就得看實際情況了。
當前併發請求數量小於限制時,就把請求直接丟到線程池中執行;否則就把請求加入到等待隊列。在代碼中搜索readyAsyncCalls,看看等待隊列中的任務是什麼時候被執行的。看到下面的方法:

// Dispatcher.java

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

搜索這個方法是在哪裏被調用的:
這裏寫圖片描述
可以看到,在任務結束、改變最大併發請求和改變域名最大併發請求時會調用該方法,這些肯定是在意料之中的。

接下來就跟蹤一下任務是如何執行的,我們就從隊列中的請求任務入手。
AsyncCall的繼承關係如下:

abstract class NamedRunnable implements Runnable
final class AsyncCall extends NamedRunnable
/**
 * Runnable implementation which always sets its thread name.
 */
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();
}

任務的執行放在了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 {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

可以看出來,主要的內容是在getResponseWithInterceptorChain()裏面,因爲這個方法直接返回了Response,也就是請求結果。

  private Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!retryAndFollowUpInterceptor.isForWebSocket()) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(
        retryAndFollowUpInterceptor.isForWebSocket()));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

這個方法的主要內容是得到一個列表interceptors,從字面上看,這個列表是用於請求攔截。通過斷點跟蹤後發現,這個列表確實重要,並且順序也是非常的重要,從下面的代碼慢慢分析。

//RealInterceptorChain.java

  @Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpStream, connection);
  }

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream, Connection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();
    、、、
    // Call the next interceptor in the chain. 
    //注意這裏的index+1了
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpStream, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    、、、
    return response;
  }

省略一些異常情況處理後,很清晰的看到,從interceptors列表的第一項(index初始化爲0)開始執行interceptor.intercept(next),並且參數是RealInterceptorChain鏈條的下一個節點。

  @Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();

    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()));

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response = null;
      boolean releaseConnection = true;
      try {
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        、、、
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          streamAllocation.streamFailed(null);
          streamAllocation.release();
        }
      }

      // Attach the prior response if it exists. Such responses never have a body.
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder().body(null).build())
            .build();
      }
      Request followUp = followUpRequest(response);
      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }

      closeQuietly(response.body());
      、、、
      request = followUp;
      priorResponse = response;
    } // end while
  }

這個方法的主體是一個死循環,有兩種方式退出循環:一個是throw new IOException(“Canceled”),另一個是return response。也就是說,當請求被取消,或者得到結果了就結束返回了。再看看response是什麼時候被賦值的:

response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);

又回到剛纔的proceed(request)裏面了,而proceed(request)裏面用到了index+1,也就是這時會使用interceptors列表中的第二個interceptor來進行intercept(next),醬紫看來,這是一個遞歸的過程。只有當進行到chain中的某一個節點,能夠得response而不是繼續向下傳遞時,遞歸開始反轉退棧。
跟蹤代碼看到interceptor的順序爲RetryAndFollowUpInterceptor——>BridgeInterceptor——>CacheInterceptor——>ConnectInterceptor——>CallServerInterceptor. 至於爲何需要這麼幾層,還不是很清楚,還需要繼續學習。

至此,okhttp的請求主流程基本介紹完畢,後面還有大量的東西需要去學習。

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