1. 程式人生 > >OkHttp原始碼流程分析

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物件

    • 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的非同步請求為什麼要維護兩個任務佇列?

    Dispatcher 生產者

    ExecutorService 消費者池

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

    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流中讀取返回資訊