天天看點

Okhttp的源碼解讀

重要的類

類名 描述
OkHttpClient OkHttp請求用戶端,Builder模式實作
Dispatcher 本質是異步請求的排程器,負責排程異步請求的執行,控制最大請求并發數和單個主機的最大并發數,并持有有一個線程池負責執行異步請求,對同步請求隻是作統計操作。
Request 封裝網絡請求,就是建構請求參數(如url,header,請求方式,請求參數),Builder模式實作
Response 網絡請求對應的響應,Builder模式實作,真正的Response是通過RealCall.getResponseWithInterceptorChain()方法擷取的。
Call 是根據Request生成的一個具體的請求執行個體,且一個Call隻能被執行一次。
ConnectionPool 連接配接池
Interceptor Interceptor可以說是OkHttp的核心功能,它就是通過Interceptor來完成監控管理,重寫和重試請求的。
Cache 可以自定義是否采用緩存,緩存形式是磁盤緩存,DiskLruCache。

源碼思想

OkHttp3,網絡請求庫,同步請求RealCall.execute()和異步請求RealCall.enqueue(),請求任務都是交給Dispatcher排程請求任務的處理,請求通過一條攔截鍊,每一個攔截器處理一部分工作,最後一個攔截器,完成擷取請求任務的響應,會将響應沿着攔截鍊向上傳遞。

源碼流程

初始化一個OkHttpClient對象
OkHttpClient mOkHttpClient = new OkHttpClient();  
           
public OkHttpClient() {  
    this(new Builder());  
}  
           

Builder是OkHttpClient的一個内部類,目的是構造和封裝資料

public Builder() {  
     dispatcher = new Dispatcher();  
     protocols = DEFAULT_PROTOCOLS;  
     connectionSpecs = DEFAULT_CONNECTION_SPECS;  
     proxySelector = ProxySelector.getDefault();  
     cookieJar = CookieJar.NO_COOKIES;  
     socketFactory = SocketFactory.getDefault();  
     hostnameVerifier = OkHostnameVerifier.INSTANCE;  
     certificatePinner = CertificatePinner.DEFAULT;  
     proxyAuthenticator = Authenticator.NONE;  
     authenticator = Authenticator.NONE;  
     connectionPool = new ConnectionPool();  
     dns = Dns.SYSTEM;  
     followSslRedirects = true;  
     followRedirects = true;  
     retryOnConnectionFailure = true;  
     connectTimeout = _000;  
     readTimeout = _000;  
     writeTimeout = _000;  
}
           
建立一個請求

Request類是HTTP請求,它攜帶了請求位址、請求方法、請求頭部、請求體以及其他資訊。它也是通過Builder模式建立的。

Request request = new Request.Builder()  
      .url(url)  
      .build(); 
           
public Request build() {  
  if (url == null) throw new IllegalStateException("url == null");  
  return new Request(this);  
}  
           
private Request(Builder builder) {  
    this.url = builder.url;  
    this.method = builder.method;  
    this.headers = builder.headers.build();  
    this.body = builder.body;  
    this.tag = builder.tag != null ? builder.tag : this;  
}  
           
接下來對應的就是請求,請求分為異步與同步,先看同步
Response  response = mOkHttpClient.newCall(request).execute();
           
Response是HTTP響應,它繼承自Closeable(Closeable繼承自AutoCloseable,AutoCloseable資源自動關閉的接口),它攜帶了請求、網絡請求協定、傳回狀态碼、請求頭、響應體等等,其中,網絡請求協定是Protocol,Protocol是一個枚舉類型,包含4中類型
public enum Protocol {  
    HTTP_1_0("http/1.0"),  
    HTTP_1_1("http/1.1"),  
    SPDY_3("spdy/3.1"),  
    HTTP_2("h2");  
}  
           
Call類是一個抽象類
OkHttpClient.newCall(request);  

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

protected RealCall(OkHttpClient client, Request originalRequest) {  
  this.client = client;  
  this.originalRequest = originalRequest;  
  this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);  
} 
           
public interface Call {  
    Request request();  
    Response execute() throws IOException;  
    void enqueue(Callback responseCallback);  
    void cancel();  
    boolean isExecuted();  
    boolean isCanceled();  
    interface Factory {  
        Call newCall(Request request);  
    }  
}  
           
OKHttp提供了execute()方法(同步方法)和enqueue()方法(異步方法),下面我們先看看execute()方法(同步方法)

execute()方法,首先判斷是否已執行過,如果已經執行過,則抛出異常資訊,也就是說一次Call執行個體隻能調用一次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();  
    if (result == null) throw new IOException("Canceled");  
    return result;  
} finally {  
    client.dispatcher().finished(this);  
} 
           
分發器将請求加入到隊列
...  
 private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//雙端隊列  
 ...  
     /** Used by {@code Call#execute} to signal it is in-flight. */  
synchronized void executed(RealCall call) {  
  runningSyncCalls.add(call);  
}  
           
最後通過分發器的finished()方法結束請求
void finished(RealCall call) {  
    finished(runningSyncCalls, call, false);  
}  

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 ==  && idleCallback != null) {  
        idleCallback.run();  
    }  
} 
           

是以通過上面資訊可知真正發送請求的地方是getResponseWithInterceptorChain()方法

真正發送請求源碼分析

組裝各種攔截器,然後發送請求

private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
    Interceptor.Chain chain = new ApplicationInterceptorChain(, originalRequest, forWebSocket);
    return chain.proceed(originalRequest);
}
           
攔截器接口
public interface Interceptor {
    Response intercept(Chain chain) throws IOException;
    interface Chain {
        Request request();
        Response proceed(Request request) throws IOException;
        Connection connection();
    }
}
           

在這個責任鍊模式中執行每一個連接配接器的proceed方法,然後我們逐個分析這些連接配接器

這裡是責任鍊模式,由使用者主動調用此方法,是以需要實作攔截的功能

這裡如果有Intercepter實作已經添加,則為每一個攔截器建立一個ApplicationInterceptorChain然後遞歸直到将所有的鍊調用完成最後進入getResponse方法中。

@Override public Response proceed(Request request) throws IOException {
  //If there's another interceptor in the chain, call that.
  if (index < client.interceptors().size()) {
  //建立一個環節,并且将索引+1,這裡的request可能不再是originalRequest了,因為在攔截器中可能被修改了
    Interceptor.Chain chain = new ApplicationInterceptorChain(index + , request, forWebSocket);
    //擷取到對應的攔截器
    Interceptor interceptor = client.interceptors().get(index);
    //執行攔截器的intercept方法,參數是上面建立的環節,這個方法裡面會調用chain.proceed(),遞歸了。
    //在最深的一層調用getResponse之後,響應會一層層的往外傳
    Response interceptedResponse = interceptor.intercept(chain);

    if (interceptedResponse == null) {
      throw new NullPointerException("application interceptor " + interceptor
          + " returned null");
    }
 // 傳回這一層攔截器處理用的響應
    return interceptedResponse;
  }

  //遞歸到了最深的一層,攔截器都進入過了,發送httpRequest,擷取到response.
  return getResponse(request, forWebSocket);
}
           

當執行完getResponse完成之後就一步步傳回。

真正請求資料的方法
  1. request.newBuilder();由于是之前的請求,是以内部調用newBuilder的時候使用老請求的url,method,body,tag,headers。然後給這個requestBuilder中添加各種請求的鍵值對資訊最後構造一個原資訊+新資訊的新請求。
  2. 建立一個請求引擎,每一個請求引擎代表依次請求/響應
  3. 在死循環中做的事情
    1. 如果請求被取消抛出異常
    2. 然後發送請求,讀取響應,重新連接配接設定成false
    3. 如果請求失敗則算了不請求了,但是如果是路由異常則需要恢複請求。
    4. 最後釋放連結,得到響應。
    5. 如果存在下一步請求,則重定向。
Response getResponse(Request request, boolean forWebSocket) throws IOException {
    // 複制請求頭,并設定适合的屬性。如果長度不為-1,則設定Content-Length,否則使用chunked方式傳輸。
    // 如果對chunked不熟悉,請參考其他資料
    RequestBody body = request.body();
    if (body != null) {
      // 根據傳進來的request建立新的Builder.
      Request.Builder requestBuilder = request.newBuilder();

      // 設定Content-Type
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      // 判斷使用何種方式傳輸
      long contentLength = body.contentLength();
      // body長度不為-1,設定Content-Length
      if (contentLength != -) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        //  body 長度為 -1 ,使用chunked傳輸
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }

      //建立新請求
      request = requestBuilder.build();
    }

    // 建立一個新的引擎,每個引擎代表一次請求/響應對
    engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);

    int followUpCount = ; //重試次數
    //死循環 出口:
    // 1. 請求被取消
    // 2. 請求有問題
    // 3. 捕獲到異常,且嘗試恢複失敗
    // 4. 擷取到響應,且無需重定向
    // 5. 重定向次數超過最大限制
    while (true) {
      // 被取消的情況
      if (canceled) {
        engine.releaseStreamAllocation();
        throw new IOException("Canceled");
      }

      boolean releaseConnection = true;
      try {
        // 發送請求
        engine.sendRequest();
        // 讀取響應
        engine.readResponse();
        releaseConnection = false;
      } catch (RequestException e) {
        // 請求失敗,請求本身有問題,或者是網絡不通
        throw e.getCause();
      } catch (RouteException e) {
        // 連接配接到伺服器的路由發生異常,請求還沒被發送
        // 通過上一次連接配接異常恢複引擎
        HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);
        // 如果恢複成功,将目前的引擎設定為這個恢複好的引擎
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }
        // 沒法恢複,抛出異常
        throw e.getLastConnectException();
      } catch (IOException e) {
        // 與伺服器互動失敗,這時,請求可能已經被發送
        // 恢複引擎
        HttpEngine retryEngine = engine.recover(e, null);
        //如果恢複成功,将目前的引擎設定為這個恢複好的引擎
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }

        // 沒法恢複,抛出異常
        throw e;
      } finally {
        // 如果需要釋放連接配接,則将連接配接釋放
        if (releaseConnection) {
          StreamAllocation streamAllocation = engine.close();
          streamAllocation.release();
        }
      }

      // 擷取響應
      Response response = engine.getResponse();
      // 下一步的請求,如果存在,則需要重定向
      Request followUp = engine.followUpRequest();

      //如果不需要重定向
      if (followUp == null) {
        if (!forWebSocket) {
          // 釋放連接配接
          engine.releaseStreamAllocation();
        }
        //傳回響應
        return response;
      }

      // 如果需要重定向,關閉目前引擎
      StreamAllocation streamAllocation = engine.close();

      // 如果超過最大數,釋放連接配接,并抛出異常
      if (++followUpCount > MAX_FOLLOW_UPS) {
        // 釋放連接配接
        streamAllocation.release();
        // 抛出異常
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      // 如果重定向的位址和目前的位址一樣,則不需要釋放連接配接
      if (!engine.sameConnection(followUp.url())) {
        streamAllocation.release();
        streamAllocation = null;
      }

      // 使用重定向後的請求,重新執行個體一個引擎,開始下一次循環
      request = followUp;
      engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,
          response);
    }
  }
           

最後HttpEngine進行的操作與http協定很大相關,作者對此不感興趣,本文隻是對流程做大概分析

哥哥若是喜歡可移駕公衆号:碼老闆

Okhttp的源碼解讀