OkHttpソースコード分析(一)要求と応答過程の簡単な分析


この記事では、OkHttpのリクエストと応答プロセスについて簡単に説明します.
記事はOkHttp 3.14.3バージョンに基づいています
0はじめに
OkHttpは現在のJava系プログラミングのネットワークリクエストライブラリとして人気があるのは言うまでもない.ネット上のOkHttpの使用、パッケージ、ソース分析に関する文章とコードもすでに百家がそろっており、腐った街と言える.それでも、筆者はOkHttpの勉強や研究を記録して自分の内容を形成し、後で見やすいようにして書き始めましょう.
それは一番簡単なことから始まります.この論文ではOkHttpにおける大まかな要求応答過程を簡単に説明するつもりである.
1開始
まず、単純な同期GETリクエストと非同期GETリクエストを見てみましょう.
  • 同期GET要求:
  • public static void getSync() {
        // Step 1.     HttpClient          
        OkHttpClient httpClient = new OkHttpClient();
        // Step 2.     Request        、    、     
        Request request = new Request.Builder().get()
                                               .url("https://www.baidu.com")
                                               .build();
        // Step 3.           Call
        Call call = httpClient.newCall(request);
        try {
            // Step 4.     
            Response response = call.execute();
            // Step 5.   、      
            ResponseBody responseBody = response.body();
            if (responseBody != null) {
                System.out.println(responseBody.string());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  • 非同期GET要求:
  • public void getAsync() {
        // Step 1.     HttpClient          
        OkHttpClient httpClient = new OkHttpClient();
        // Step 2.     Request        、    、     
        Request request = new Request.Builder().get()
                                               .url("https://www.baidu.com")
                                               .build();
        // Step 3.           Call
        Call call = httpClient.newCall(request);
        // Step 4.     
        call.enqueue(new Callback() {
            @Override
            public void onFailure(final Call call, final IOException e) {
                e.printStackTrace();
            }
    
            @Override
            public void onResponse(final Call call, final Response response) throws IOException {
                // Step 5.   、      
                ResponseBody responseBody = response.body();
                if (responseBody != null) {
                    System.out.println(responseBody.string());
                }
            }
        });
    
    }

    同期要求でも非同期要求でもStep 1~Step 3の3つのステップで要求タスクを構築し、call.execute()/call.enqueue(callback)を呼び出すことで同期/非同期要求を実行する必要があることがわかる.では、この2つの方法の実行過程を見てみましょう.
    まずcall.execute()を見てみましょう.
    Callクラスを表示するには、Callがインタフェースであることを確認し、call.execute()メソッドの特定のインプリメンテーションにジャンプしようとします(ps:ASショートカットCtrl+Alt+Bはメソッドの特定のインプリメンテーションに迅速にジャンプします)、Callインタフェースの唯一のインプリメンテーションクラスRealCallクラスに来て、RealCall.execute()は次のようにインプリメンテーションされます.
    @Override public Response execute() throws IOException {
      // Step 1.
      synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
      }
      // Step 2.
      transmitter.timeoutEnter();
      transmitter.callStart();
      try {
        // Step 3.
        client.dispatcher().executed(this);
        // Step 4.
        return getResponseWithInterceptorChain();
      } finally {
        // Step 5.
        client.dispatcher().finished(this);
      }
    }
  • Step 1.要求を実行する前に、call要求タスクがまだ実行されていないことを確認します.これにより、OkHttpは、要求されたタスクごとに1回しか実行できないことを要求していることがわかる.
  • Step 2.タイムアウト時間の計算を開始し、要求を記録してcallStartというプロセスを開始します.
  • Step 3.Dispatcherを使用して、同期要求キューrunningSyncCallsにこの要求タスクを記録します.runningSyncCallsはArrayDequeとして実装された双方向キューです:
  • /**            ,               . */
    private final Deque runningSyncCalls = new ArrayDeque<>();
    synchronized void executed(RealCall call) {
      runningSyncCalls.add(call);
    }
  • Step 4.次にRealCall.getResponseWithInterceptorChain()を呼び出して要求を開始し、要求結果を取得する.
  • Step 5.結果が上位層に呼び出される前に、DispatcherによってrunningSyncCallsキューからこのタスクを削除し、このリクエストタスクが終了することを示す:
  • .
  • Step 6.要求結果を上位に戻し、要求を終了する.

  • 以上のいくつかのステップを経て,一度の同期GET要求は終了する.RealCall.getResponseWithInterceptorChain()メソッドは、具体的なHTTPリクエストを担当していることがわかります.ここではしばらくフォローしません.まず、非同期GETリクエストを見てみましょう.Ctrl+Alt+B call.enqueue(callback)の特定のインプリメンテーションRealCall.enqueue(callback):
    @Override public void enqueue(Callback responseCallback) {
      // Step 1.
      synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
      }
      // Step 2.
      transmitter.callStart();
      // Step 3.
      client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }
  • Step 1.同期GET要求のStep 1と同じ.
  • Step 2.要求プロセスを記録します.
  • Step 3.AsyncCallオブジェクトを作成し、リクエストキューに追加します.AsyncCallはRealCallの内部クラスであり、非同期のリクエストタスクを表すために使用されます.AsyncCallの親であるNamedRunnableは、Runnableインタフェースを実装する抽象クラスであり、Runnable.runメソッドを書き換え、execute()抽象メソッドを外部に提供し、runメソッド実装で呼び出す.そこで、次に、AsyncCallクラス実装のexecute()メソッドに注目する.AsyncCallはRunnableであり、runではexecute()が呼び出されている以上、非同期要求タスクは最終的にここまで実行され、実際のHTTP要求:
  • が実行されると判定することができる.
    @Override protected void execute() {
      boolean signalledCallback = false;
      // Step 3.1           
      transmitter.timeoutEnter();
      try {
        // Step 3.2            
        Response response = getResponseWithInterceptorChain();
        signalledCallback = true;
        // Step 3.3     ,              
        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 {
        // Step 4.        ,       
        client.dispatcher().finished(this);
      }
    }
    }

    executeの実装を調べると、この判定が正しいことも検証され、いつ、どこから、このexecuteメソッドが実行されるかは、まず小さな穴を残してから記入しましょう.同時に、RealCall.execute()メソッド(同期要求)とAsyncCall.execute()メソッド(非同期要求)を比較すると、同期または非同期要求にかかわらず、最後にgetResponseWithInterceptorChain()メソッドを呼び出すことによって、ネットワーク要求が求められ、返された結果が得られることがわかります.このメソッドの実装を見てみましょう.
    Response getResponseWithInterceptorChain() throws IOException {
      //             
      List interceptors = new ArrayList<>();
      //            
      interceptors.addAll(client.interceptors());
      //                 
      interceptors.add(new RetryAndFollowUpInterceptor(client));
      //                  ,                   ,                  
      interceptors.add(new BridgeInterceptor(client.cookieJar()));
      //           
      interceptors.add(new CacheInterceptor(client.internalCache()));
      //                    
      interceptors.add(new ConnectInterceptor(client));
      if (!forWebSocket) {
        //          websocket  ,       websocket      ,            
        interceptors.addAll(client.networkInterceptors());
      }
      //          ,                    
      interceptors.add(new CallServerInterceptor(forWebSocket));
        //         chain  
      Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
                                                         originalRequest, this, client.connectTimeoutMillis(),
                                                         client.readTimeoutMillis(), client.writeTimeoutMillis());
    
      boolean calledNoMoreExchanges = false;
      try {
        //            
        Response response = chain.proceed(originalRequest);
        if (transmitter.isCanceled()) {
          closeQuietly(response);
          throw new IOException("Canceled");
        }
        return response;
      } catch (IOException e) {
        calledNoMoreExchanges = true;
        throw transmitter.noMoreExchanges(e);
      } finally {
        if (!calledNoMoreExchanges) {
          transmitter.noMoreExchanges(null);
        }
      }
    }

    この方法は最終的にchain.proceed(originalRequest)によって行われることがわかる.リクエストとリターン結果を実現し,ここでOkHttpのリクエストプロセスは終了する.したがって、OkHttpの要求プロセスは、概ね次のようになる.
    まずここまでにしましょう.一編書きたいと思っていたのに、長すぎて自分でも見たくないようなので、一歩一歩進んでみましょう.次編では,以上のOkHttpリクエストの過程で触れた各キークラスの解析に重点を置く.
    2 The End :)
    公衆番号へようこそ:
    記事は個人ブログに初登場https://www.nullobject.cn、公衆番号
    NullObject同期更新.