Okhttp 3ソースコード解析(非同期同期要求)

19631 ワード

Okhttp 3ソースコード解析(非同期同期要求)
本編では主にOkhttp 3のリクエストフローをコードフローにより分析する.okhttp 3の使用は皆さんが把握していると信じていますが、ここでは省略します.okhttp 3に基づく.9.1.
同期リクエストの例:
OkHttpClient client=new OkHttpClient();     //     OkHttpClient
        //Request request= new Request.Builder().build();
        Request request = new Request.Builder()                //   Request ,        
                .url("https://api.github.com/repos/square/okhttp/issues")
                .header("User-Agent", "OkHttp Headers.java")
                .addHeader("Accept", "application/json; q=0.5")
                .addHeader("Accept", "application/vnd.github.v3+json")
                .build();

        Response response = null;            
        try {
            response = client.newCall(request).execute();     //  Response 
        } catch (IOException e) {
            e.printStackTrace();
        }

OkHttpClientすべてのプロセスの総制御者
OkHttpClientを開き、公式の説明を見てみましょう.
Factory for {@linkplain Call calls}, which can be used to send HTTP requests and read their responses.

HTTPリクエストを送信し、応答を読み出すために使用できるcalls/callのファクトリ.
各callの作成には、OkHttpClientをインスタンス化する必要があります.同時に、OkHttpClientはリクエストのプロセスを全体的に制御します.
Request request = new Request.Builder()
ここではrequestのリクエストセットで、私たちがリクエストした様々な情報がカプセル化されています.
response = client.newCall(request).execute();
キーコードresponse=client.newCall(request).execute(); ここでは同期要求を行い、execute()はresponse情報を直接返します.clientは総プロセスの制御者であり、ここではclientによってnewCallを作成します.
newCall
newCallは何ですか?コードを開けてみてください.
@Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

Clientでは、RealCall.newRealCall(this,request,false/*for web socket*/)を介してcallオブジェクトが作成されます.作成が行われたので、このプロセスはRealCallに移行しました.
RealCallリクエストの実際の呼び出し元
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);   //    RealCall
    call.eventListener = client.eventListenerFactory().create(call);  //????
    return call;
  }

newRealCallは静的な方法であり、パケット内アクセスに限られるため、newRealCall()を直接呼び出すことはできません.ここでは、RealCallオブジェクトがCallインタフェースを実装しているため、自身のコンストラクション関数を呼び出すことでRealCallオブジェクトを返すことができます.
call.eventListener = client.eventListenerFactory().create(call);

それからこのコードは頭がつかめません.RealCallに戻った以上、この関数はどういう意味ですか.コードを見て、このeventListenerがクライアントを通じて呼び出されていることに気づきました.そうしないと、どうしてプロセス全体の制御者になりますか.eventListenerFactory()という工場に来てみてください.
public EventListener.Factory eventListenerFactory() {
    return eventListenerFactory;
  }

eventListenerFactoryに直接戻って、???このeventListenerFactoryは初期化されていませんが、どうやって実現しますか?eventListenerFactoryを検索すると、Builderに次のようなコードがあることがわかります.
 public Builder() {
      ...
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      ...
    }

EventListener
EventListener.NONE??何の鬼か、空の類に伝わって何をしているのか.EventListenerを開く
 Listener for metrics events. Extend this class to monitor the quantity, size, and duration of your application's HTTP calls.

公式の説明を見て、アプリケーションのHTTP呼び出しの数、サイズ、持続時間を監視するためにクラスを拡張するイベントのリスナーを見てみましょう.
一瞬にして釈然としたが,もともと定義されていない空のクラスであり,具体的な場面に基づいてイベントを傍受し,このクラスを継承することによってその方法を実現することができる.
では、クライアントも初期化され、realcallも手に入れられ、requstもカプセル化されました.では、次は何をしますか.同期要求を行いますか.
response = client.newCall(request).execute();
execute()メソッドは、実際にはRealCallのexecute()メソッドであり、上記で説明したように、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);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }

execute()
同じように、一歩一歩分析します.
synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }

executedはboolean変数であり、executed要求が行われるとtrueに設定されるため、RealCallは1回だけexecuted()要求を開始することができます.
captureCallStackTrace();
この関数を見て、まず意味から分析し、callの異常スタックをキャプチャします.
private void captureCallStackTrace() {
    Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
    retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
  }

retryAndFollowUpInterceptorは、Platformプラットフォームを介して「response.body().close()の異常スタックについては、Interceptorについて以下に説明します.
eventListener.callStart(this);
eventListener?見覚えがあるような気がしますが、正しいのは私たちの上の空のクラスで、このクラスを継承することでリクエストイベントの傍受を実現することができます.ここではデフォルトがnoneなので、あまり効果はありません.
client.dispatcher().executed(this);
Client、やっぱり何もあなたから離れられない、clirent.dispatcher()は、ここではdispatcherクラスについても紹介します.dispatcherはokHttpCilentのbuilderで初期化されます.つまり、OkhttpClientはdispatcherを持っています.dispacherは表面を通じて配布者の意味を意味します.ここでは、配布者によって同期executed()メソッドを実行する.
Dispatcher
Policy on when async requests are executed.

公式の説明は、非同期管理を実行するポリシー、すなわち、非同期要求の実行クラスである.
中には次のような変数があります.
 /** Ready async calls in the order they'll be run. */
  private final Deque readyAsyncCalls = new ArrayDeque<>();   //         

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */   
  private final Deque runningAsyncCalls = new ArrayDeque<>();     //           

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque runningSyncCalls = new ArrayDeque<>();         //        

Excuted()メソッド
次に彼のexecuted方法を見てみましょう.
synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);     // call        
  }
try {
      client.dispatcher().executed(this);        //       。
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");     //             ,  
      return result;                            //      
    } catch (IOException e) {
      eventListener.callFailed(this, e);    //    ,    
      throw e;
    } finally {
      client.dispatcher().finished(this);   
    }

executed()メソッドが実行された以上、次はResponseを得るメソッド、getResponseWithInterceptorChain()である.
Response getResponseWithInterceptorChain() throws IOException {         //        
    // Build a full stack of interceptors.
    List 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);
  }

上記の方法は、実際には、様々なブロックを介して、入力されたoriginalRequestを介して順次反復処理され、ここでは責任チェーンの処理モードを用い、同時にコード全体をデカップリングし、RecycleViewの処理のように、各モジュールは自分の部分に注目するだけである.
OkHttpClientの構成時に設定されるinterceptors、すなわちカスタムinterceptors.失敗した再試行とリダイレクトを担当するRetryAndFollowUpInterceptor.ユーザ構造の要求をサーバに送信する要求に変換し,サーバから返される応答をユーザフレンドリーな応答に変換するBridgeInterceptorを担当する.キャッシュを直接読み込み、キャッシュを更新するCacheInterceptor.サーバとの接続を担当するConnectInterceptor.OkHttpClientの設定時に設定したnetworkInterceptors.要求データをサーバに送信し、応答データをサーバから読み出すCallServerInterceptor.
最後にchainが呼び出されたことに気づきます.proceed(originalRequest);各ブロッカーのproceed()メソッドでは、このメソッドが呼び出され続け、すべてのブロッカーを巡る役割を果たします.
最後にfinalyでは次のような方法があります.dispatcher().finished(this); これはリクエスト全体の終了段階であり、コードは以下の通りである.
private  void finished(Deque 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 == 0 && idleCallback != null) {     
      idleCallback.run();
    }
  }

これは汎用関数であり、入力されたcallsのdequeによって、この操作が完了したため、callを直接削除することができ、ここでまた新しいインタフェースRunnable idleCallbackが現れた.これはまた何なのか、類の中に来て見てみましょう.
public synchronized void setIdleCallback(@Nullable Runnable idleCallback) {
    this.idleCallback = idleCallback;
  }

このインタフェースに入力する関数は1つしかありません.idleCallbackは、現在タスクが実行されていないときに呼び出す方法として提供されている空のインタフェースです.上のrunningCallsCount=runningCallsCount();タスクがまだ行われているかどうかを判断するために使用され、現在の同期タスクが完了すると、このインタフェースの関数が呼び出され、いくつかの終了処理に使用されます.
同期リクエストは大体このようにして、次に非同期リクエストを見てみましょう
非同期リクエスト
OkHttpClient client=new OkHttpClient();
        //Request request= new Request.Builder().build();
        Request request = new Request.Builder()
                .url("https://api.github.com/repos/square/okhttp/issues")
                .header("User-Agent", "OkHttp Headers.java")
                .addHeader("Accept", "application/json; q=0.5")
                .addHeader("Accept", "application/vnd.github.v3+json")
                .build();
        client.newCall(request).enqueue(new Callback() {   //      
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });

同様に非同期リクエストを行い、結果を直接返すことはできないため、完了または失敗を受け入れるためにcallインタフェースに転送する必要があります.同様に、RealCallのenqueueメソッドに直接来ました.
@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);              //       ,       
    client.dispatcher().enqueue(new AsyncCall(responseCallback));  //    

同様にクライアントのdispatcherによってリクエストの配布が行われる.dispatcherのenqueueメソッドに来ました.
synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

dispatcherではexecutorServiceスレッドプールが維持されています.前述したように、非同期リクエストが同時に存在するrunningAsyncCalls()とreadyAsyncCalls()は、まずスレッドプールの数を判断する必要があります.現在実行中のスレッドが最大リクエスト数より小さい場合は、同時に、各ホストの最大要求数がこのときのホスト要求の数より大きいことは、現在のホストが要求を行うことができることを示し、callを直接進行中のキューに追加し、executorService()を通過する.execute(call)はcallのメソッドを実行します.現在空き場所がない場合は、待機する必要がありますので、readyAsyncCallsに追加し、次の呼び出しを待機します.