Okhttp3源碼淺析

發起一個簡單的Okhttp請求:

OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
        
Request request = new Request
                .Builder()
                .url("https://www.baidu.com")
                .build();
okhttp3.Call call = okHttpClient.newCall(request);
 
////同步請求
//call.execute();

//異步請求
 call.enqueue(new Callback() {
      @Override
       public void onFailure(Call call, IOException e) {
           Log.e("tag", "請求失敗");
           e.printStackTrace();
       }

       @Override
       public void onResponse(Call call, Response response) {
           Log.e("tag", "請求成功");
       }
 });

構建Okhttp的幾個基礎類:
Request:請求方法、請求頭、參數、請求體等基礎參數的封裝類
OkhttpClient:用於構建Call請求的工廠類,用於配置請求如超時時間、緩存、https證書驗證、攔截器等,內部還封裝了用於分發請求的Dispatcher類、Socket工廠類、Socket連接池等。
Call:請求的發起、取消、結果返回、狀態回調,是Okhttp請求的控制類

Okhttp請求執行的基本流程如下圖:
在這裏插入圖片描述

同步請求

Call的實現類RealCall調用execute()發起同步請求,接着調用getResponseWithInterceptorChain()方法獲取同步響應結果,此方法的具體實現後面統一分析

 @Override
 public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.timeoutEnter();
    transmitter.callStart();
    try {
      client.dispatcher().executed(this);
      return getResponseWithInterceptorChain();
    } finally {
      client.dispatcher().finished(this);
    }
  }
異步請求

RealCall調用enqueue()方法發起異步請求,實際上是通過其內部保存的OkhttpClient引用,獲取到請求分發器Dispacher,創建RealCall的內部類AsyncCall對象(其實是一個Runnable對象),添加到請求隊列中

class RealCall{
	 ......
	 @Override public void enqueue(Callback responseCallback) {
	    synchronized (this) {
	      if (executed) throw new IllegalStateException("Already Executed");
	      executed = true;
	    }
	    transmitter.callStart();
	    client.dispatcher().enqueue(new AsyncCall(responseCallback));
	  }
	 .......
 }

接下來看一下Dispatcher中的實現,它內部封裝了線程池、待執行的、執行中的同步/異步請求隊列以及請求數量的限制條件

public final class Dispatcher {
	  private int maxRequests = 64;
	  private int maxRequestsPerHost = 5;
	  private @Nullable Runnable idleCallback;
	
	  private @Nullable ExecutorService executorService;
	  //待執行的異步請求隊列
	  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
	  //正在執行的異步請求
	  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
	  //正在執行的同步請求
	  private final Deque<RealCall> 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;
	  }
	  
	  //添加請求到待執行隊列
	  void enqueue(AsyncCall call) {
	    synchronized (this) {
	      readyAsyncCalls.add(call);
	
	      // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
	      // the same host.
	      if (!call.get().forWebSocket) {
	        AsyncCall existingCall = findExistingCallWithHost(call.host());
	        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
	      }
	    }
	    promoteAndExecute();
	  }

	  private boolean promoteAndExecute() {
		    assert (!Thread.holdsLock(this));
		
		    List<AsyncCall> executableCalls = new ArrayList<>();
		    boolean isRunning;
		    synchronized (this) {
		      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
		        AsyncCall asyncCall = i.next();
		        //最大支持同時64個異步請求
		        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
		        //同一host下最大支持同時5個異步請求
		        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
		
		        i.remove();
		        //統計同一host下同時請求個數
		        asyncCall.callsPerHost().incrementAndGet();
		        executableCalls.add(asyncCall);
		        runningAsyncCalls.add(asyncCall);
		      }
		      isRunning = runningCallsCount() > 0;
		    }
		
		    for (int i = 0, size = executableCalls.size(); i < size; i++) {
		      AsyncCall asyncCall = executableCalls.get(i);
		      asyncCall.executeOn(executorService());
		    }
		
		    return isRunning;
	  }
	 ......
 }

接着看異步請求的執行流程,上述代碼第30行Dispatcher調用enqueue()方法將AsyncCall添加到待執行的請求隊列,第41行調用promoteAndExecute()真正將請求添加到線程池執行。

從方法內部可以看到Okhttp對於併發數量是有限制的,最大同時執行的請求數量爲64個,同一host下最多可以同時執行5個請求。

最後遍歷可執行的請求,邏輯從Dispatcher類中切換到AsyncCall中,AsyncCall本身又是一個Runnable,它的最終執行邏輯一定是在run()方法中實現的,跟蹤代碼可以看到它的邏輯切換流程executeOn(executorService) --> run() --> execute()

@Override
protected void execute() {
      boolean signalledCallback = false;
      transmitter.timeoutEnter();
      try {
        //發起請求返回響應結果
        Response response = getResponseWithInterceptorChain();
        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 {
          //請求失敗的回調
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
 }

這裏的代碼和同步請求RealCall中execute()非常相似,同樣是調用getResponseWithInterceptorChain()獲取響應結果,只不過異步請求的Response回調執行在子線程中。

攔截器(Interceptor)

無論是同步請求還是異步請求都是調用getResponseWithInterceptorChain()獲取響應結果,方法命名很直觀,通過攔截器鏈來獲取響應。

  Response getResponseWithInterceptorChain() throws IOException {
    //將各種攔截器添加到集合
    List<Interceptor> interceptors = new ArrayList<>();
    //添加自定義的請求攔截器
    interceptors.addAll(client.interceptors());
    //添加失敗重試和重定向攔截器
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    //添加網絡橋攔截器,用戶添加默認的請求頭等信息
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //添加緩存攔截器
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //添加連接攔截器,給當前請求創建或者從緩存裏獲取一個可用連接(建立socket連接)
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      //添加自定義的網絡攔截器
      interceptors.addAll(client.networkInterceptors());
    }
    //添加請求服務攔截器,真正發起請求通過io流獲取響應信息(通過socket連接發送數據以及接受響應)
    interceptors.add(new CallServerInterceptor(forWebSocket));

    //構建攔截器鏈的頭節點
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    boolean calledNoMoreExchanges = false;
    try {
      /*
       * 開始攔截器鏈的執行,根據index獲取攔截器執行intercept()方法,intercept()內部通過自增的index參數構建新的攔截器鏈節點,
       * 通過其調用proceed()又開啓下一個攔截器的處理,這樣就構成了一個完整的循環遍歷執行攔截器的過程
       */
      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);
      }
    }
  }
}

方法內部定義了Interceptor集合依次將以下攔截器添加到集合中:
自定義Interceptor:我們自己實現的攔截器,比如我們有時候想要在請求前統一添加公共參數或者添加請求、響應的日誌都可以自定義攔截器來實現;
RetryAndFollowUpInterceptor:實現了失敗重試和重定向的一些策略;
BridgeInterceptor:處理我們添加的請求頭以及添加一些默認請求頭信息如gzip透明壓縮等;
CacheInterceptor:處理緩存策略的一些邏輯;
ConnectInterceptor:創建Socket和服務器進行連接,維護Socket連接池;有興趣的朋友可以跟以下這個攔截器的代碼它最終創建Socket連接實在RealConnection類中實現的
自定義NetworkInterceptor:通常用於監聽Socket連接內容,如Facebook出品的Stetho,可以很方便的讓我們通過瀏覽器調試接口
CallServerInterceptor:向Socket連接發送請求數據並且獲取響應數據,它底層用Okio庫替代了Java原生的io流操作。

getResponseWithInterceptorChain()方法內部創建了一個重要的對象RealInterceptorChain,它的構造函數會傳入一個index,這個index正是下一個所要執行的攔截器在集合中的下標,process()方法每調用依次index都會隨之遞增,而每個Interceptor的intercept()方法中又會調用RealInterceptorChain的process()方法,這樣就達到了遍歷執行Intercepter的目的

public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
      throws IOException {
    ......
    // 執行下一個攔截器的代碼
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
        index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    ......

    return response;
  }

在這裏插入圖片描述
如上圖所示,攔截器鏈在遍歷執行的過程中,依次對Request進行攔截處理最後獲取到響應後又沿着相反的方向將Response返回,在返回的過程中攔截器又可以逐層最Response進行處理,將最終結果返回給Call對象,這樣就完成一個完整的Okhttp請求流程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章