1. 程式人生 > >Android網路框架:OKHttp原始碼簡單解析(一)

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;//最大併發請求數為64
  private int maxRequestsPerHost = 5;//每個主機最大請求數為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進行快取等待。