Okhttp細解之一

       今天來拆解一下okhttp;分析一下okhttp如何做到這麼好;以及我們需要掌握什麼樣的技能或者思路去實現一個自己的“okhttp”;那先從入口開始看;

      1.OkHttpClient  ;先看頭部信息下面備註了;


//Cloneable 淺copy ,Call.Factory實現工廠接口(針對於普通接口)WebSocket.Factory實現工廠接口(針對websocket請求的接口)
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {

}

        a、我們現在先搞明白上面實現接口的目的;第一個Cloneable這個是淺copy使用的;暫時真的沒感覺OkHttpClient能在什麼場合使用到;但是這個還是提供了;Call.Factory =>主要是實現RealCall這個類;這個類裏面是實現普通的網絡請求的方式;WebSocket.Factory =>主要實現RealWebSocket這個類;這個類裏面實現webscoket的請求方式;

        b、看OkHttpClient的參數是什麼;下面都進行備註了;

  static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
      Protocol.HTTP_2, Protocol.HTTP_1_1);//創建一個只讀的;不可以修改的list;設置默認的 http/2 ,http/1.1

  static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
      ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT);//創建一個只讀的;不可以修改的list;TLS配置
  static {
    Internal.instance = new Internal() {//創建一些方法裏面實現一些需要做的方法;後續講
       ...
    };
  }
  final Dispatcher dispatcher;//開非核心線程進行,異步耗時操作(網絡請求)
  final Proxy proxy;//默認空值
  final List<Protocol> protocols;//設置網絡類型
  final List<ConnectionSpec> connectionSpecs;//設置TLS的
  final List<Interceptor> interceptors;//攔截器,僅支持初始化設置
  final List<Interceptor> networkInterceptors;//網絡攔截器,僅支持初始化設置
  final ProxySelector proxySelector;//設置安全的認證操作
  final CookieJar cookieJar;//默認沒有cookies
  final Cache cache;//緩存
  final InternalCache internalCache;//網絡緩存
  final SocketFactory socketFactory;//創建一個對象並將其連接到指定遠程端口上的指定遠程地址
      // 對象還將綁定到提供的本地地址和端口。使用爲此工廠建立的對象選項配置對象。
      //如果有安全管理器,checkConnect 則使用主機地址port 及其參數調用其方法。這可能會導致SecurityException。
  final SSLSocketFactory sslSocketFactory;//設置保護HTTPS連接的工廠
  final CertificateChainCleaner certificateChainCleaner;//設置限制哪些證書受信任的證書
  final HostnameVerifier hostnameVerifier;//註冊認證的方式
  final CertificatePinner certificatePinner;//反抓包
  final Authenticator proxyAuthenticator;//返回一個請求,該請求包含滿足{@code response}中的身份驗證的憑據。如果無法滿足條件,則返回null,默認沒有
  final Authenticator authenticator;//返回一個請求,該請求包含滿足{@code response}中的身份驗證的憑據。如果無法滿足條件,則返回null。默認沒有

  final ConnectionPool connectionPool;//連接池 = 線程池 + 消息隊列
  final Dns dns;//DNS配置
  final boolean followSslRedirects;//是否允許Ssl重定向,默認true
  final boolean followRedirects;//是否允許重定向,默認true
  final boolean retryOnConnectionFailure;//是否連接失敗時重試,默認true
  final int connectTimeout;//連接超時時間,默認10_000(10秒)
  final int readTimeout;//讀取超時時間,默認10_000(10秒)
  final int writeTimeout;//寫入超時時間,默認10_000(10秒)
  final int pingInterval;//ping的時間間隔 默認 0

那麼我們從OkHttpClient裏面看到幾個重要點:看一下能夠調用的方法;發現到都是獲取配置;並沒有設置配置的接口;所以對應的OkHttpClient並沒有直接提供對應的設置;

所以OkhttpClient初始化OkHttpClient(Builder builder) 藉助於內部類Builder進行設置;

那麼看一下這個內部類的builder的參數

  Dispatcher dispatcher;
    Proxy proxy;
    List<Protocol> protocols;//設置默認的 http/2 ,http/1.1
    List<ConnectionSpec> connectionSpecs;//TLS配置
//下面和上面的okhttp一樣
    final List<Interceptor> interceptors = new ArrayList<>();
    final List<Interceptor> networkInterceptors = new ArrayList<>();
    ProxySelector proxySelector;
    CookieJar cookieJar;
    Cache cache;
    InternalCache internalCache;
    SocketFactory socketFactory;
    SSLSocketFactory sslSocketFactory;
    CertificateChainCleaner certificateChainCleaner;
    HostnameVerifier hostnameVerifier;
    CertificatePinner certificatePinner;
    Authenticator proxyAuthenticator;
    Authenticator authenticator;
    ConnectionPool connectionPool;
    Dns dns;
    boolean followSslRedirects;
    boolean followRedirects;
    boolean retryOnConnectionFailure;
    int connectTimeout;
    int readTimeout;
    int writeTimeout;
    int pingInterval;

初始化方法Builder()(這個是默認初始化的)和Builder(OkHttpClient okHttpClient)(可以將上次的okHttpClient設置的東西傳遞回去)

所以這個時候,先初始化一個builder類,再去重新設置該builder裏面的部分方法;或者參數

那麼我在builder裏面挑出幾個重要的點來講吧;

2、首先Dispatcher這個類很重要;首先這個類幹這幾件事情:

   1、創建非核心線程池executorService -》爲之後的網絡請求做異步處理;

    2、設置了readyAsyncCalls隊列(準備請求,開始網絡請求數大於閥值64,以及不能有相同正在請求的host數量超過5個),這個其實就是緩存暫時不請求的接口;在請求完畢之後留在重新觸發;

    3、設置runningAsyncCalls隊列;異步請求的;

    4、設置了runningSyncCalls隊列;同步請求的;

    5、所以總的在請求數是runningAsyncCalls.size()+runningSyncCalls.size();

   

看一下圖;裏面的enqueue(AsyncCall call);添加進去做一個判斷(異步操作)

(runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost)

runningAsyncCalls.add(call);//請求添加到隊列

executorService().execute(call);//開始進行異步請求

如果添加在準備隊列裏面,當請求結束的時候;回調該方法

 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();//是否將ready裏面的值加到run裏面(異步)
      runningCallsCount = runningCallsCount();//獲取請求的數量
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();//當無網絡請求之後都會進行一次回調
    }
  }

這樣的話,就完成了;一次網絡請求;這個講解的是異步以及是Dispatcher類;並不涉及真正的網絡請求之類的;

下面在說明一下同步請求的方式:

這個就涉及到RealCall裏面的execute()方法;會調用到.executed(this);然後添加進去

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

結束的時候也會調用finish的方法;這個就不涉及到是否進行最大值多少個之類的;來了就是請求;

我們現在來看一下請求的觸發點;當然我們直講重要的點RealCall

/*
 * Copyright (C) 2014 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package okhttp3;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import okhttp3.internal.NamedRunnable;
import okhttp3.internal.cache.CacheInterceptor;
import okhttp3.internal.connection.ConnectInterceptor;
import okhttp3.internal.connection.StreamAllocation;
import okhttp3.internal.http.BridgeInterceptor;
import okhttp3.internal.http.CallServerInterceptor;
import okhttp3.internal.http.RealInterceptorChain;
import okhttp3.internal.http.RetryAndFollowUpInterceptor;
import okhttp3.internal.platform.Platform;

import static okhttp3.internal.platform.Platform.INFO;

final class RealCall implements Call {
  final OkHttpClient client;
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;

  /** The application's original request unadulterated by redirects or auth headers. */
  final Request originalRequest;
  final boolean forWebSocket;

  // Guarded by this.
  private boolean executed;

  RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

  @Override public Request request() {
    return originalRequest;
  }

  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }

  private void captureCallStackTrace() {
    Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
    retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
  }

  @Override public void enqueue(Callback responseCallback) {//異步請求的方式
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));//觸發異步請求;線程池
  }

  @Override public void cancel() {
    retryAndFollowUpInterceptor.cancel();
  }

  @Override public synchronized boolean isExecuted() {
    return executed;
  }

  @Override public boolean isCanceled() {
    return retryAndFollowUpInterceptor.isCanceled();
  }

  @SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
  @Override public RealCall clone() {
    return new RealCall(client, originalRequest, forWebSocket);
  }

  StreamAllocation streamAllocation() {
    return retryAndFollowUpInterceptor.streamAllocation();
  }

  final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

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

  /**
   * Returns a string that describes this call. Doesn't include a full URL as that might contain
   * sensitive information.
   */
  String toLoggableString() {
    return (isCanceled() ? "canceled " : "")
        + (forWebSocket ? "web socket" : "call")
        + " to " + redactedUrl();
  }

  String redactedUrl() {
    return originalRequest.url().redact();
  }

  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 (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

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

我們分別看一下請求的方式(異步和同步)

 1、enqueue(Callback responseCallback) --異步方式

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

    final class AsyncCall extends NamedRunnable {
    ...

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

 2、 Response execute()---同步的方式

//同步方法 
@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();//真正網絡請求的地方
      if (result == null) throw new IOException("Canceled");
      return result;//返回值
    } finally {
      client.dispatcher().finished(this);
    }
  }

所以重點是Response getResponseWithInterceptorChain()這個方法體;

我們現在去瞧瞧:

 Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());//先添加client提供的攔截器,build裏面才能添加;這個就是我們自定義的攔截器;這個是還沒有請求的攔截器
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

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

其中攔截器這個類是:

//攔截器都需要實現這個類
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;//其中proceed是觸發下面一個類執行的條件

    Connection connection();
  }
}

其中我們還需要看一下RealInterceptorChain這類:

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

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      Connection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

   ...

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);//設置下一個攔截器的Chain,從0開始

       ...

    return response;
  }

那麼我們從FIRST開始看;觸發了proceed;那麼如果沒有添加我們的攔截器;直接觸發RetryAndFollowUpInterceptor攔截器;

public final class RetryAndFollowUpInterceptor implements Interceptor {
    ...
  @Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();

    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()), callStackTrace);//複用和創建連接池的操作;這個在OkhttpClient裏面實現方法;

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
    ...
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);//上面的代碼實現;將連接池設置進去
       ...
  }

...
}

這個創建連接池之後開始proceed;那麼又調用RealInterceptorChain生成下一個攔截器的chain並且調用intercept

BridgeInterceptor填充cookie等,再次調用chain.proceed(requestBuilder.build());這個不需要在傳遞連接池;

然後在次觸發CacheInterceptor,一樣的再次觸發ConnectInterceptor,CallServerInterceptor,觸發之後在依次執行之前的proceed之後的方法;當然這個是繼續執行;最終回到RetryAndFollowUpInterceptor裏面執行followUpRequest然後返回response;

然後我們進行回調等處理;這樣不管是異步還是同步;走的方式都是責任鏈的;

之後會繼續講解一下里面實現的細節;比如如何創建scoket等等

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