Okhttp3源碼分析之一

一、OKHTTP簡介

OKHttp是一個處理網絡請求的開源項目OKHttp GitHub地址

OKHttp優點

  1. 支持HTTP2/SPDY(SPDY是Google開發的基於TCP的傳輸層協議,用以最小化網絡延遲,提升網絡速度,優化用戶的網絡使用體驗。)
  2. socket自動選擇最好路線,並支持自動重連,擁有自動維護的socket連接池,減少握手次數,減少了請求延遲,共享Socket,減少對服務器的請求次數。
  3. 基於Headers的緩存策略減少重複的網絡請求。
  4. 擁有Interceptors輕鬆處理請求與響應(自動處理GZip壓縮)。

OKHttp的功能

  1. PUT,DELETE,POST,GET等請求
  2. 文件的上傳下載
  3. 加載圖片(內部會圖片大小自動壓縮)
  4. 支持請求回調,直接返回對象、對象集合
  5. 支持session的保持

二、OkHttp使用

此處只舉例異步調用:

        //1.創建OkHttpClient對象
        OkHttpClient okHttpClient = new OkHttpClient();
        //2.創建Request對象,設置一個url地址(百度地址),設置請求方式。
        Request request = new Request.Builder().url("http://www.baidu.com").method("GET",null).build();
        //3.創建一個call對象,參數就是Request請求對象
        Call call = okHttpClient.newCall(request);
        //4.請求加入調度,重寫回調方法
        call.enqueue(new Callback() {
            //請求失敗執行的方法
            @Override
            public void onFailure(Call call, IOException e) {
            }
            //請求成功執行的方法
            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });

上面就是發送一個異步GET請求的4個步驟:

  1. 創建OkHttpClient對象
  2. 通過Builder模式創建Request對象,參數必須有個url參數,可以通過Request.Builder設置更多的參數比如:header、method等
  3. 通過request的對象去構造得到一個Call對象,Call對象有execute()和cancel()等方法。
  4. 以異步的方式去執行請求,調用的是call.enqueue,將call加入調度隊列,任務執行完成會在Callback中得到結果。

注意事項:

  1. 異步調用的回調函數是在子線程,我們不能在子線程更新UI,需要藉助於 runOnUiThread() 方法或者 Handler 來處理。
  2. onResponse回調有一個參數是response,如果我們想獲得返回的是字符串,可以通過response.body().string()獲取;如果希望獲得返回的二進制字節數組,則調用response.body().bytes();如果你想拿到返回的inputStream,則調response.body().byteStream(),有inputStream我們就可以通過IO的方式寫文件

三、OkHttpClient源碼分析

1、先看內部類和構造函數的實現

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
  //協議,支持http2和http1
  static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
      Protocol.HTTP_2, Protocol.HTTP_1_1);

  //連接規範,帶有擴展的現代TLS連接 和 未加密、未經身份驗證的連接
  //具體作用先不看
  static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
      ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);

  static {
    Internal.instance = new Internal() {
       //加入請求頭
      @Override public void addLenient(Headers.Builder builder, String line) {
        builder.addLenient(line);
      }
        //加入請求頭
      @Override public void addLenient(Headers.Builder builder, String name, String value) {
        builder.addLenient(name, value);
      }

        //設置緩存
      @Override public void setCache(OkHttpClient.Builder builder, InternalCache internalCache) {
        builder.setInternalCache(internalCache);
      }

       //連接置空閒,兩個參數ConnectionPool(連接池),RealConnection(真實連接)
       //在連接池把真實連接置爲空閒狀態
      @Override public boolean connectionBecameIdle(
          ConnectionPool pool, RealConnection connection) {
        return pool.connectionBecameIdle(connection);
      }

       //獲取真實連接,通過address(地址)、StreamAllocation(流分配)、Route (線路)
       //在連接池獲取當前真實連接
      @Override public RealConnection get(ConnectionPool pool, Address address,
          StreamAllocation streamAllocation, Route route) {
        return pool.get(address, streamAllocation, route);
      }

       //判斷兩個主機是否相同
      @Override public boolean equalsNonHost(Address a, Address b) {
        return a.equalsNonHost(b);
      }

      //消除重複連接
      @Override public Socket deduplicate(
          ConnectionPool pool, Address address, StreamAllocation streamAllocation) {
        return pool.deduplicate(address, streamAllocation);
      }

        //加入連接池
      @Override public void put(ConnectionPool pool, RealConnection connection) {
        pool.put(connection);
      }

        //線路集合
      @Override public RouteDatabase routeDatabase(ConnectionPool connectionPool) {
        return connectionPool.routeDatabase;
      }

       //響應狀態碼
      @Override public int code(Response.Builder responseBuilder) {
        return responseBuilder.code;
      }

       //
      @Override
      public void apply(ConnectionSpec tlsConfiguration, SSLSocket sslSocket, boolean isFallback) {
        tlsConfiguration.apply(sslSocket, isFallback);
      }

        //無效地址
      @Override public boolean isInvalidHttpUrlHost(IllegalArgumentException e) {
        return e.getMessage().startsWith(HttpUrl.Builder.INVALID_HOST);
      }

       //流分配
      @Override public StreamAllocation streamAllocation(Call call) {
        return ((RealCall) call).streamAllocation();
      }

        //異常
      @Override public @Nullable IOException timeoutExit(Call call, @Nullable IOException e) {
        return ((RealCall) call).timeoutExit(e);
      }

        //創建新的call
      @Override public Call newWebSocketCall(OkHttpClient client, Request originalRequest) {
        return RealCall.newRealCall(client, originalRequest, true);
      }
    };
  }

 final Dispatcher dispatcher;//任務調度,執行異步請求時的策略
  final @Nullable Proxy proxy;//代理
  final List<Protocol> protocols;//所支持的協議
  final List<ConnectionSpec> connectionSpecs;//連接規範
  final List<Interceptor> interceptors;//攔截器
  final List<Interceptor> networkInterceptors;//網絡攔截器
  final EventListener.Factory eventListenerFactory;//負責監聽整個連接請求的生命週期
  final ProxySelector proxySelector;//代理選擇器
  final CookieJar cookieJar;//cookie緩存
  final @Nullable Cache cache;//響應報文緩存
  final @Nullable InternalCache internalCache;//OKhttp內部緩存
  final SocketFactory socketFactory;//創建socket的工廠
  final SSLSocketFactory sslSocketFactory;//ssl的socket工廠繼承於SocketFactory 
  final CertificateChainCleaner certificateChainCleaner;//證書的獲取和清除
  final HostnameVerifier hostnameVerifier;//主機認證
  final CertificatePinner certificatePinner;//該類用於約束哪些證書是可信的。 鎖定證書可以防止對證書頒發機構相關的攻擊。 它還阻止通過用戶已知或未知的中間證書頒發機構建立的連接
  final Authenticator proxyAuthenticator;//認證器
  final Authenticator authenticator;//
  final ConnectionPool connectionPool;//連接池
  final Dns dns;//dns獲取ip,OKHTTP嘗試的順序排列。如果與地址的連接失敗,OKHTTP將重試與下一個地址的連接,直到要麼建立連接,要麼耗盡IP地址集,要麼超過限制
  final boolean followSslRedirects;//遵循SSL重定向
  final boolean followRedirects;//遵循重定向
  final boolean retryOnConnectionFailure;//重試連接失敗
  final int callTimeout;//請求超時
  final int connectTimeout;//連接超時
  final int readTimeout;//讀超時
  final int writeTimeout;//寫超時
  final int pingInterval;//ping 間隔時長

  public OkHttpClient() {
    this(new Builder());
  }

  OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.eventListenerFactory = builder.eventListenerFactory;
    this.proxySelector = builder.proxySelector;
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;

    boolean isTLS = false;
    for (ConnectionSpec spec : connectionSpecs) {
      isTLS = isTLS || spec.isTls();
    }

    if (builder.sslSocketFactory != null || !isTLS) {
      this.sslSocketFactory = builder.sslSocketFactory;
      this.certificateChainCleaner = builder.certificateChainCleaner;
    } else {
      X509TrustManager trustManager = Util.platformTrustManager();
      this.sslSocketFactory = newSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }

    if (sslSocketFactory != null) {
      Platform.get().configureSslSocketFactory(sslSocketFactory);
    }

    this.hostnameVerifier = builder.hostnameVerifier;
    this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
        certificateChainCleaner);
    this.proxyAuthenticator = builder.proxyAuthenticator;
    this.authenticator = builder.authenticator;
    this.connectionPool = builder.connectionPool;
    this.dns = builder.dns;
    this.followSslRedirects = builder.followSslRedirects;
    this.followRedirects = builder.followRedirects;
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
    this.callTimeout = builder.callTimeout;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval;

    if (interceptors.contains(null)) {
      throw new IllegalStateException("Null interceptor: " + interceptors);
    }
    if (networkInterceptors.contains(null)) {
      throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
    }
  }

Okhttp3源碼分析之二

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