OkHttp工做流程

OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
Request request = new Request.Builder().build();
Call newCall = okHttpClient.newCall(request);
//同步請求
//Response response = newCall.execute();
//異步請求
newCall.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
    }
});
複製代碼
  • 異步請求接口的Callback中的成功失敗接口回調是運行在子線程

execute()總結

public Response execute() throws IOException {
    synchronized (this) {
      //判斷當前call是否執行過,是的話拋異常
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      //將當前call添加到Dispatcher正在執行的任務隊列
      client.dispatcher().executed(this);
      //經過一系列攔截器鏈作網絡請求,拿到response
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      //從正在執行的任務隊列中移除當前call
      client.dispatcher().finished(this);
    }
  }
複製代碼
  • 判斷當前call是否執行過,是的話拋異常
  • 將當前call添加到Dispatcher正在執行的任務隊列
  • 經過一系列攔截器鏈作網絡請求,拿到response
  • 從正在執行的任務隊列中移除當前call

異步enqueue()總結

> RealCall類enqueue()方法

public void enqueue(Callback responseCallback) {
    synchronized (this) {
      //判斷當前call是否執行過,是的話拋異常
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }
複製代碼
  • 判斷當前call是否執行過,是的話拋異常緩存

  • 將callBack封裝成一個AsyncCall對象bash

    • AsyncCall繼承NamedRunnable,定義在RealCall中的內部類
    • NamedRunnable是Runnable的一個實現類,Runnable的run()方法內部會調用到本身類中的一個抽象方法execute(),最終實如今AsyncCall中
      > AsyncCall類execute()方法具體實現
      
      protected void execute() {
            boolean signalledCallback = false;
            try {
              //經過一系列攔截器鏈作網絡請求,拿到response
              Response response = getResponseWithInterceptorChain();
              //經過判斷重定向重試攔截器是否被取消了,是的話就調用responseCallback.onFailure()
              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 {
                //發生異常也會調用失敗回調
                eventListener.callFailed(RealCall.this, e);
                responseCallback.onFailure(RealCall.this, e);
              }
            } finally {
              //將當前請求call對象從正在運行任務隊列中移除
              client.dispatcher().finished(this);
            }
          }
      複製代碼
  • 調用client.dispatcher().enqueue()服務器

    > Dispatcher類enqueue()方法
    
    //判斷正在運行異步任務隊列大小是否小於最大請求數(64)而且經過runningCallsForHost()方法獲取到正在運行的異步任務隊列中和當前call所要請求的主機同樣的調用數來判斷是否小於最大請求主機數(5)
    //若是知足上述條件 則將當前call添加到正在運行異步任務隊列中,不然添加到等待異步任務隊列
    synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          //添加到異步任務隊列
          runningAsyncCalls.add(call);
          //開啓線程池執行當前call
          //executorService()方法內部會判斷線程池是否已經建立,是的話直接返回,否的話建立線程池
          //execute() 未來某個時候執行給定的任務,任務能夠在新線程或現有池中已存在的線程執行
          //其實就是把AsyncCall(線程的實現類)對象放到線程池中,最後真正執行的就是AsyncCall對象的execute()方法
          executorService().execute(call);
        } else {
          //添加到異步等待任務隊列
          readyAsyncCalls.add(call);
        }
      }
    複製代碼

Dispatcher

  • 什麼是Dispatcher?網絡

    dispatcher的做用是維護請求的狀態,並維護一個線程池,用於執行請求異步

  • Dispatcher的異步請求爲何要維護兩個任務隊列?ide

    Dispatcher 生產者ui

    ExecutorService 消費者池this

    • runningAsyncCalls 正在執行異步請求隊列,包含沒有執行完的請求但已經被取消了
    • readyAsyncCalls 就緒狀態異步請求隊列
    • executorService 執行請求的線程池
  • executorService()spa

    public synchronized ExecutorService executorService() {
        if (executorService == null) {
          //corePoolSize:0 核心線程數.0的話就表示在空閒一段時間(keepAliveTime)後,會將所有線程銷燬
          //maximumPoolSize:Integer.MAX_VALUE 線程池容許建立最大線程數;理論上設置MAX_VALUE能夠無限擴充建立線程,因爲OKHttp有maxRequests(64)限制,實際並不能無限建立線程
          //keepAliveTime:60 空閒線程最大存活時間.當咱們的線程數大於核心線程數,多餘的空閒線程最大存活時間
          //3個參數含義:當線程池中任務執行完畢以後,會在60秒以後相繼關閉全部空閒線程
          executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
              new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
        }
        return executorService;
      }
    複製代碼
  • 移除任務call線程

    同步請求和異步請求在拿到Response以後都會調用finished()方法

    > 如下方法全是在Dispatcher類中
    
    //異步請求
    void finished(AsyncCall call) {
        finished(runningAsyncCalls, call, true);
    }
    //同步請求
    void finished(RealCall call) {
        finished(runningSyncCalls, call, false);
    }
    
    private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
        int runningCallsCount;
        Runnable idleCallback;
        synchronized (this) {
          //從當前任務隊列中移除當前call
          if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
          //調整異步請求任務隊列 只有異步請求才會執行promoteCalls()
          if (promoteCalls) promoteCalls();
          //從新計算正在執行任務數:異步請求+同步請求任務數量之和
          runningCallsCount = runningCallsCount();
          idleCallback = this.idleCallback;
        }
    
        if (runningCallsCount == 0 && idleCallback != null) {
          idleCallback.run();
        }
    }
    /**
    異步請求隊列從新調度.從等待隊列中移除一個任務,添加到正在執行異步隊列中
    */
    private void promoteCalls() {
        if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
        if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
    
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
          AsyncCall call = i.next();
    
          if (runningCallsForHost(call) < maxRequestsPerHost) {
            i.remove();
            runningAsyncCalls.add(call);
            executorService().execute(call);
          }
          //若是正在執行異步隊列中數量大於等於最大請求數(64),直接結束調度
          if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
        }
    }
    複製代碼

攔截器

攔截器是OkHttp中提供一種強大機制,它能夠實現網絡監聽、請求以及響應重寫、請求失敗重試等功能

  • RetryAndFollowUpInterceptor

    重試,失敗重定向攔截器

    • 建立StreamAllocation對象
    • 調用RealInterceptorChain.proceed()方法進行網絡請求
    • 根據異常結果獲取響應結果判斷是否要從新請求
    • 調用下一個攔截器,對Response進行處理
  • BridgeInterceptor

    橋接適配攔截器,處理請求缺乏必要的http請求頭相關信息

    • 負責將用戶構建的一個Request請求轉化爲可以進行網絡訪問的請求
    • 將這個符合網絡請求的Request進行網絡請求
    • 將網絡請求回來的響應Response轉化爲用戶可用的Response
  • CacheInterceptor

    緩存攔截器,經過DiskLRUCache實現緩存存取,OkHttp內部維護清理線程池,會自動清理緩存文件

  • ConnectInterceptor

    鏈接攔截器,創建可用的鏈接

    • ConnectInterceptor獲取Interceptor傳遞過來的StreamAllocation,streamAllocation.newStream()
    • 將剛纔建立用於網絡IO的RealConnection對象,以及對於與服務器交互最爲關鍵的HttpCodec等對象傳遞後面的Interceptor

    newStream()總結

    • 獲取到一個RealConnection
    • 選擇不一樣的連接方式
  • CallServerInterceptor

    • 將http請求寫入到網絡的IO流當中,從網絡io流中讀取返回信息
相關文章
相關標籤/搜索