天天看點

Android網絡架構:OKHttp源碼簡單解析(一)

這是第一次解析源碼并把它寫出來,在之前,我一直以為隻要會用别人的輪子就好,做出實際的效果就行,對看源碼對自己的能力提升不以為然。後來偶然聽到一句話:看别人的DEMO,你就可以會用輪子,但是要想用好輪子,還是得看源碼。我覺得看源碼有兩個方面的好處:

1. 從本質上去了解所學架構的原理和流程;
 2. 可以看到軟體開發裡常見的思維方法和設計模式;這些在程式設計裡都是相通的。
           

下面這篇文章,我們隊OkHttp架構做一個簡單的分析。

使用OkHttp通路網絡,通用的做法就是下面四步:

步驟: 
 //1 建立okHttpClient對象
 //2 建立網絡Request對象
 //3 建立與Request對應的傳回Call對象
 //4 請求加入排程(直接執行)
           

在第四步中,通路網絡有兩種方式,一種是同步請求通路網絡。一種是異步請求通路網絡,我們先看看同步通路網絡是怎麼實作的。

同步通路網絡實作源碼解析:它是通過okHttpClient.newCall(re)建立一個Call對象。如下:

OkHttpClient okHttpClient =new OkHttpClient();
 Response response = okHttpClient.newCall(request).execute();
 Request request = new Request.Builder()
                .url("http://publicobject.com/helloworld.txt")
                .build();
 Call call=okHttpClient.newCall(request);
 Response response = call.execute();
 if(response.isSuccessful()){             Log.i("TAG","response.code()="+response.code());//200           Log.i("TAG","response.body().string()="+response.body().string());
        }
           

這裡我們先看看Call對象到底是什麼鬼?跟蹤源碼我們可以看到:

/**
   * Prepares the {@code request} to be executed at some point in the future.//準備一個在未來某個點會被執行的網絡請求
   */
  @Override public Call newCall(Request request) {
    return new RealCall(this, request);
  }
           

可以看到真正起作用的是RealCall,并非我們看到的Call;我們再看看RealCall是什麼?

final class RealCall implements Call {//RealCall是一個繼承了Call接口的網絡真正請求類,
  private final OkHttpClient client;
 ....
  protected RealCall(OkHttpClient client, Request originalRequest) {
    this.client = client;
    this.originalRequest = originalRequest;
  }
//真正執行execute的地方;
  @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(false);
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }
           

上面代碼中,client.dispatcher().executed(this);是将目前網絡請求,從OkHttpClient獲得的Dispatcher然後把它加入到分發器裡面的隊列 executedCalls中,在完成的時候會remove掉,源碼如下:

synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

  synchronized void finished(Call call) {
    if (!runningSyncCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
  }
           

上面中的runningSyncCalls是指正在執行的同步任務隊列,在OkHttp架構中對網絡請求進行分發任務的是Dispatcher類,Dispatcher維護了如下變量,用于控制并發的請求

private int maxRequests = ;//最大并發請求數為64
  private int maxRequestsPerHost = ;//每個主機最大請求數為5
  private ExecutorService executorService;//線程池
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();//維護準備執行的異步請求隊列,雙端隊列緩存(用數組實作,可自動擴容,無大小限制);
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//正在執行的異步任務隊列,包括已經取消但是并未finish的請求
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//正在執行的同步任務隊列,包括已經取消但是并未finish的請求
           

Dispatcher: 分發者,也就是生産者(預設在主線程)

AsyncCall: 隊列中需要處理的Runnable(包裝了異步回調接口)

ExecutorService:消費者池(也就是線程池)

根據生産者消費者模型的模型理論,當入隊(enqueue)請求時,如果滿足(runningRequests<64 && runningRequestsPerHost<5),那麼就直接把AsyncCall直接加到runningCalls的隊列中,并線上程池中執行。如果消費者緩存滿了,就放入readyAsyncCalls進行緩存等待。