OKHttp 3-Displatchの配布器が同期非同期要求のソースコード解析を実現する【3】


Displatch
  • シリーズ
  • 回顧
  • Displatch
  • データ構造
  • スレッド池
  • 管理を要求する
  • .
  • ソースコード
  • シリーズ
    OKHttp 3–詳しい使用とソース分析シリーズの初歩紹介【一】OKHttp 3–プロセス分析核心類紹介同期要求ソース分析【二】OKHttp 3–Disp配布器のソース解析【三】OKHttp 3–呼び出し先RealCallのソース解析【四】OKHttp 3–ブロックチェーンRealInterorptorinソース解析【5】OKHttp 3–再試行とリダイレクトブロックRetryAndFollowUpInterceptorソース解析【6】OKHttp 3–ブリッジブロックBridgeInterceptorソース解析および関連するSthtp要求ヘッダフィールド解析【7】OKHttp 3–キャッシュインターセプターソース解析【8】OKhttp 3ソースソース解析システム【九】
    回顧する
    前の分析OKHttp同期同期要求文章では、大体Dispactchという種類を紹介しました.OKHttpでの地位は非常に重要です.必ずその役割を理解してください.全体の枠組みのタスクスケジューラとして、疑問があります.
  • Displatchは一体何をしていますか?
     OKHttp Dispatcher                  (RealCall)     (AsyncCall),
         AsyncCall
    
  • OKHttpは、同期及び非同期要求をどのように実現するか?
        ,   Dispatcher            
    
          ,      runningSyncCalls (           );
                  
    
          ,    AsyncCall              runningAsyncCalls  (           )
     readyAsyncCalls  (           ),    AsyncCall;      ,     ,
              
    
  • Displatch
    ここではまずソースコードを置かないで、まず何か特徴がありますか?
    データ構造
    Dispactchでは三つの列を維持しています.列のタイプはArayDequeです.これはダブルエンドの列です.つまり、列の頭と尾からデータ操作ができます.FIFO原則を実現できます.先に入ると実行できます.スレッドの安全が実現されるのではなく、マルチスレッド環境にロックが必要です.AyncTaskソースを確認してください.具体的な詳細はソースから参照してください.解析-Androidデータ構造の双端行列ArayDequeはFIFOとLIFO行列を実現します.
  • の最初のキューは、ランニングSyncCallsであり、実行中の同期要求キューであり、私たちが追加した同期要求は全部この中に追加されます.キャンセルされましたが、実行されていない要求を含めて、キューの一般化はRealCallオブジェクト
  • です.
  • の第二の列は、ルニー・アッセイ・カレルズであり、実行中の非同期要求キューであり、追加された非同期要求はここに追加されます.キャンセルされたが、実行されていない要求を含めて、キュー・汎型はAyncCallオブジェクトであり、Runnableインターフェース
  • を実現しました.
  • の第3のキューはreadyAryncCallsで、実行待ちの非同期要求キューとはどういう意味ですか?OKHttpが同時に実行できる非同期要求の数は64個以内であり、単独のhostが同時に実行する最大要求の数は5個以内である必要がありますので、追加された非同期要求数がそれを超えると、この要求はキューに追加されます.ここでは、ホスト名(ホスト名を表しています.各ホストは一意の識別があります.ipアドレスですが、ホスト名は必ずしも唯一のものとは限りません.)同じサーバに同時に送信される要求の数は5つを超えてはいけないと理解できますが、OKHttpはURLのホスト名で判断されていますので、同じipアドレスの同時要求は5つを超える可能性があります.複数のホスト名が同じIPアドレスまたはルーティング(同じHTTPエージェント)
  • を共有する可能性があります.
    これらの列を使うと何に使いますか?メリットはどこにありますか?
    これらのキューを通して、OKHttpは容易に同時要求、より便利なメンテナンス要求数、およびこれらの要求に対する後続の操作を実現することができることを知るべきである(キャンセル要求など).ネットワーク要求の効率を大幅に向上させるとともに、同時に実行するスレッドが多すぎることを防止し、OOMを招いて、同じhostname下の要求数を制限し、アプリケーションが占有するネットワークリソースが多すぎることを防止し、ユーザー体験を最適化する.
    スレッドの池
    OKHttpはその内部に非同期要求AyncCallを実行するスレッド池を保持し、その構造は以下の通りである.
    private ExecutorService executorService;
    public synchronized ExecutorService executorService() {
      if (executorService == null) {
        executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
            new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
      }
      return executorService;
    }
    
    私たちが注目したいのは、ThreadPoolExectorの前の3つのパラメータです.
  • の最初のものは0であり、このスレッドはコアスレッドがないと説明されています.すべてのスレッドは作業スレッドです.つまり、すべてのスレッドは一定の空き時間を超えると回収されます.
  • の2番目のパラメータは、Integer.MAXUVALEであり、最大スレッド数である.設定値はこんなに大きいが、性能の消耗が大きいと心配する必要はない.キューがあるので、維持要求数は
  • である.
  • の第3のパラメータは60であり、すなわちワークスレッドがアイドル状態になると
  • が回収される.
    スレッド池については、Android開発を参照してください.ExectorServiceを通じてAPPで使用されるグローバルスレッドプールを構築します.
    管理を要求する
    private int maxRequests = 64;
    private int maxRequestsPerHost = 5;
    
    クラスでは、これらの2つの変数が定義されており、デフォルトでサポートされている最大の同時要求数は64個であり、単一のhost同時要求の最大数は5個である.この2つの値は、後続の設定によって変更されることができ、この要求は、非同期要求に対してのみ適用され、同ステップの要求数については限定されない.
    非同期要求がDispactchに入ると、上記の2つの数量の要求を満たすと、この要求はルニーAyncCallsに追加され、それを実行します.満足しない場合はreadyAryncCallsに追加します.非同期要求が終了すると、readyAyAryncCalls列を巡回して条件判断を行います.条件に該当すると、キューからAcyncCallingsに移動します.列に並べて実行します
    同期要求であろうと、非同期要求であろうと、最終的に実行されたら、キューから削除されます.
    追加と削除に加えて、OKHttpは追加された要求のキャンセルをサポートしています.すべてキャンセルして、列が空になります.また、ある要求をキャンセルしてもいいです.
    ソース
    最後にソースでDisplatchを知ります.
    public final class Dispatcher {
      //       
      private int maxRequests = 64;
      //           
      private int maxRequestsPerHost = 5;
      
      private Runnable idleCallback;
    
      /**   AsyncCall     */
      private ExecutorService executorService;
    
      /**            . */
      private final Deque readyAsyncCalls = new ArrayDeque<>();
    
      /**            ,              */
      private final Deque runningAsyncCalls = new ArrayDeque<>();
    
      /**            ,              */
      private final Deque runningSyncCalls = new ArrayDeque<>();
    
      //            
      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(), Util.threadFactory("OkHttp Dispatcher", false));
        }
        return executorService;
      }
    
      /**
       *             
       */
      public synchronized void setMaxRequests(int maxRequests) {
        if (maxRequests < 1) {
          throw new IllegalArgumentException("max < 1: " + maxRequests);
        }
        this.maxRequests = maxRequests;
        promoteCalls();
      }
    
      public synchronized int getMaxRequests() {
        return maxRequests;
      }
    
      /**
       *                 
       */
      public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) {
        if (maxRequestsPerHost < 1) {
          throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost);
        }
        this.maxRequestsPerHost = maxRequestsPerHost;
        promoteCalls();
      }
    
      public synchronized int getMaxRequestsPerHost() {
        return maxRequestsPerHost;
      }
    
      /**
       *            ,          ,    
       */
      public synchronized void setIdleCallback(Runnable idleCallback) {
        this.idleCallback = idleCallback;
      }
    
      /**
       *       
       *               64   host           5   ,      ,      
       *           
       */
      synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          runningAsyncCalls.add(call);
          executorService().execute(call);
        } else {
          readyAsyncCalls.add(call);
        }
      }
    
      /**
       *       
       */
      public synchronized void cancelAll() {
        for (AsyncCall call : readyAsyncCalls) {
          call.get().cancel();
        }
    
        for (AsyncCall call : runningAsyncCalls) {
          call.get().cancel();
        }
    
        for (RealCall call : runningSyncCalls) {
          call.cancel();
        }
      }
    
      //      ,                  
      private void promoteCalls() {
        if (runningAsyncCalls.size() >= maxRequests) return; //                ,       .
        if (readyAsyncCalls.isEmpty()) return; //          ,      
    
        //      
        for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
          AsyncCall call = i.next();
          //  host           5   ,       runningAsyncCalls     
          //           
          if (runningCallsForHost(call) < maxRequestsPerHost) {
            i.remove();
            runningAsyncCalls.add(call);
            executorService().execute(call);
          }
    
          if (runningAsyncCalls.size() >= maxRequests) return; //                ,     
        }
      }
    
      /**     host     */
      private int runningCallsForHost(AsyncCall call) {
        int result = 0;
        for (AsyncCall c : runningAsyncCalls) {
          if (c.host().equals(call.host())) result++;
        }
        return result;
      }
    
      /**       ,           */
      synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
      }
    
      /**           . */
      void finished(AsyncCall call) {
        finished(runningAsyncCalls, call, true);
      }
    
      /**           . */
      void finished(RealCall call) {
        finished(runningSyncCalls, call, false);
      }
    
      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();
        }
      }
    
      /**                   . */
      public synchronized List queuedCalls() {
        List result = new ArrayList<>();
        for (AsyncCall asyncCall : readyAsyncCalls) {
          result.add(asyncCall.get());
        }
        return Collections.unmodifiableList(result);
      }
    
      /**                 . */
      public synchronized List runningCalls() {
        List result = new ArrayList<>();
        result.addAll(runningSyncCalls);
        for (AsyncCall asyncCall : runningAsyncCalls) {
          result.add(asyncCall.get());
        }
        return Collections.unmodifiableList(result);
      }
    
      //             
      public synchronized int queuedCallsCount() {
        return readyAsyncCalls.size();
      }
    
      //           
      public synchronized int runningCallsCount() {
        return runningAsyncCalls.size() + runningSyncCalls.size();
      }
    }
    
    Displatchのソースコードは比較的簡単で、理解が難しくなく、鍵を把握する必要があります.その役割はユーザーの要求を管理し、保存し、バックアップすることです.重要なのはスレッド池を使って非同期要求を実行することです.同期要求に対してはほとんど処理しません.