OkHttp3.9要求プロセスソースの解析


まずはOkHttpの基礎を見てみましょう


通常の同期getリクエストコードを作成するには、次のようにします.
OkHttpClient client = new OkHttpClient();//1
String run(String url) throws IOException {
    Request request = new Request.Builder().url(url).build();//2
    Response response = client.newCall(request).execute();//3
    if (response.isSuccessful()) {
        return response.body().string();
    } else {
        throw new IOException("Unexpected code " + response);
    }
}

非同期get:
OkHttpClient client = new OkHttpClient();//1
    Request request = new Request.Builder()
            .url("http://xxxxxx")
            .build();//2
    client.newCall(request).enqueue(new Callback() {//3
        @Override
        public void onFailure(Call call, IOException e) {
        }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if(response.isSuccessful()){// 。
                
            }
        }
    });

すべて主要な3つのステップです:1.OkHttpClientインスタンスを作成します.2.コンストラクタを使用してリクエストを作成します.3.要求を発行します.

OkHttpClient構築方法


構築方法からOkHttpClientの作成手順を見てみましょう
public OkHttpClient() {
    this(new Builder());
  }
public 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 = systemDefaultTrustManager();
      this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }

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

作成プロセスは主にコンストラクタの構造方法を呼び出し、いくつかのリクエストに関連するパラメータを初期化しています.この中にはほとんどHTTPリクエストに関連するパラメータがあります.深く追及しないでください.主にインターネットptorsとnetworkInterceptorsの3つのものに注意してください.この2つのクラスはこの文章のテーマです.後で詳しく説明します.ここではOkHttpリクエストのタスクスケジューリングの原理を知ることができます.
public final class Dispatcher {
  private int maxRequests = 64;// 
  private int maxRequestsPerHost = 5;// host 
  
  public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  public Dispatcher() {
  }

  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
  ...
  
 }

Dispatcherはスケジューリングセンターのようなもので、ここではすべてのタスクの実行を担当しています.注目すべきは、executorServiceというオブジェクトがスレッドプールであり、サイズを制限しないスレッドプールであることです.ここではSynchronousQueue(サイズを緩和しないブロックキュー)が使用されています.このスレッドプールはExecutorsのデフォルトのnewCachedThreadPoolと原理が同じです.タスクが追加されると空きスレッドが多重化され、なければ新しいスレッドが作成され、各スレッドが空き後60秒で破棄されます.

リクエストの作成

Request request = new Request.Builder()
            .url("http://xxxxxx")
            .build();

このステップは簡単で、リクエストアドレスを入力すればOKで、あまり言うことはありません.

リクエストの開始


ここで本文に着いて、OkHttpの要求がどのように実行されたのかを見て、まず同期の要求を見てみましょう.
Response response = client.newCall(request).execute();

client.newCall(request):
  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

RealCallを呼び出します.newRealCall:
  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

RealCallの構造に戻ります
  private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

ここではいくつかのオブジェクトを初期化する以外に、もう一つ重要なものがあります.RetryAndFollowUpInterceptor、またInterceptorを見ました.前のOkHttpの構造にもinterceptorsとnetworkInterceptorsがあります.名前を見ても彼らがきっとどんな関係があるか知っています.具体的には、私たちはまず伏線を買って、RealCallの同期ネットワーク要求execute方法を見続けます.
RealCall.execute
  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);//1
      Response result = getResponseWithInterceptorChain();//2
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }

ここまで来ると、一度の同期要求が実行され、OkHttp設計者の強さに感嘆せざるを得ない.APIの呼び出しは簡単明瞭で、ソースコードもこのように一気に完成した.では、具体的にどのように処理されているかを見てみましょう.コアコードはtryコードブロックの2行にあります.

1. client.dispatcher().executed(this)


ここでのdispatcher()は、以前に重点的に述べたスケジューラであり、executedメソッドを見てみましょう.
Dispatcher.executed
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

簡単に言えば、ランタイムキューにこのリクエストを入れて、ついでにこのキューを見てみましょう.
  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

注釈がはっきり書いてあるので、翻訳しません.

2. Response result = getResponseWithInterceptorChain();


ここでまたInterceptor関連のものを見て、ここに着いて、矢は弦の上で、出さなければならなくて、やっとOkHttpの最も核心的な遮断器のメカニズムを引き出すことができます.

Interceptor


まず、ブロッキングの定義を見てみましょう.
Javaのブロッキングは、アクション呼び出しを動的にブロックするオブジェクトです.開発者が1つのaction実行の前後で実行するコードを定義したり、1つのaction実行前に実行を阻止したりするメカニズムを提供し、actionの再利用可能な部分を抽出する方法も提供します.AOP(Aspect-oriented Programming)では、メソッドまたはフィールドがアクセスされる前にブロッキングを行い、前または後に何らかのアクションを加えるためにブロッキングが使用されます.
OkHttpでは,ブロックは要求の各段階でいくつかの処理を行い,その後伝達して実行を継続する.OkHttpのブロッキングはいったいどういうことなのか、さっきのgetResponseWithInterceptorChainの方法に戻ってみましょう.
RealCall.getResponseWithInterceptorChain
  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, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

タイトル発表


ここで見た、interceptorsは実は1つの配列で、OkHttp構造の中でもこの配列とnetworkInterceptorsを初期化して、それではこの2つの配列はいったいどんな違いがありますか?少し幅があるような気がしますが、次はその答えを解きましょう.