從源碼的角度分析 OKHttp3 (一) 同步、異步執行流程

前言

因爲以前項目搭建的是 MVP 架構,由RxJava + Glide + OKHttp + Retrofit 等開源框架組合而成,以前也都是停留在使用層面上,沒有深刻的研究,最近打算把它們所有攻下,尚未關注的同窗能夠先關注一波,看完這個系列文章,(不論是面試仍是工做中處理問題)相信你都在知道原理的狀況下,處理問題更加駕輕就熟。java

Android 圖片加載框架 Glide 4.9.0 (一) 從源碼的角度分析 Glide 執行流程git

Android 圖片加載框架 Glide 4.9.0 (二) 從源碼的角度分析 Glide 緩存策略github

從源碼的角度分析 Rxjava2 的基本執行流程、線程切換原理web

從源碼的角度分析 OKHttp3 (一) 同步、異步執行流程面試

從源碼的角度分析 OKHttp3 (二) 攔截器的魅力json

從源碼的角度分析 OKHttp3 (三) 緩存策略設計模式

從源碼的角度分析 Retrofit 網絡請求,包含 RxJava + Retrofit + OKhttp 網絡請求執行流程數組

介紹

OKHttp 出至於 移動支付 Square 公司, 適用於AndroidKotlinJava 的 HTTP 客戶端,我的認爲就目前來講 OKHttp 是最好用之一的網絡請求框架。在網絡請求中使用 OKhttp 好處也是顯而易見的,好比以下幾點:緩存

    1. 支持 HTTP2 / SPDY
    1. Socket 自動選擇最好路線,並支持自動重連
    1. 擁有自動維護的 Socket 鏈接池,減小握手次數
    1. 擁有隊列線程池,輕鬆寫併發
    1. 擁有 Interceptors 輕鬆處理請求與響應(好比透明 GZIP 壓縮)基於 Headers 的緩存策略
    1. HTTP / 2支持容許對同一主機的全部請求共享一個套接字
    1. 響應緩存徹底避免網絡重複請求

源碼分析

在介紹源碼以前請先看基本的 GetPost 使用安全

Get 同步

public OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  try (Response response = client.newCall(request).execute()) {
    return response.body().string();
  }
}
複製代碼

POST 異步

public final MediaType JSON = MediaType.get("application/json; charset=utf-8");

public OkHttpClient client = new OkHttpClient();

public void post(String url, String json) {
	RequestBody body = RequestBody.create(JSON, json);
	Request request = new Request.Builder()
					.url(url)
					.post(body)
					.build();

	client.newCall(request).enqueue(new Callback() {
	@Override
  public void onFailure(Call call, IOException e) {
                
	}

	@Override
  public void onResponse(Call call, Response response) throws IOException {

	}
	});
}
複製代碼

源碼分析

在進入 OKHttp 源碼以前先看一下各自執行的流程

同步時序圖

uo7xNn.png

異步時序圖

uTPau9.png

代碼講解

經過上面時序圖能夠知道,其實不論是異步仍是同步,最後都是在 getResponseWithInterceptorChain(); 函數中處理 RequestResponse 能夠說 getResponseWithInterceptorChain() 這個函數是整個處理請求和響應的核心,這個咱們待會再詳細講解,下面咱們來解析 OKHttp 代碼執行流程:

  1. 根據同步、異步代碼示例,首先建立一個 OkHttpClient

    //建立 OkHttpClient
    OkHttpClient okHttpClient = new OkHttpClient();
    複製代碼
    public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
      
      ...//省略部分代碼
      
      public OkHttpClient() {
        this(new Builder());
      }
        public Builder newBuilder() {
            return new Builder(this);
        }
    
        public static final class Builder {
            Dispatcher dispatcher; //調度器
            /** * 代理類,默認有三種代理模式DIRECT(直連),HTTP(http代理),SOCKS(socks代理) */
            @Nullable Proxy proxy;
            /** * 協議集合,協議類,用來表示使用的協議版本,好比`http/1.0,`http/1.1,`spdy/3.1,`h2等 */
            List<Protocol> protocols;
            /** * 鏈接規範,用於配置Socket鏈接層。對於HTTPS,還能配置安全傳輸層協議(TLS)版本和密碼套件 */
            List<ConnectionSpec> connectionSpecs;
            //攔截器,能夠監聽、重寫和重試請求等
            final List<Interceptor> interceptors = new ArrayList<>();
            final List<Interceptor> networkInterceptors = new ArrayList<>();
            EventListener.Factory eventListenerFactory;
            /** * 代理選擇類,默認不使用代理,即便用直連方式,固然,咱們能夠自定義配置, * 以指定URI使用某種代理,相似代理軟件的PAC功能 */
            ProxySelector proxySelector;
            //Cookie的保存獲取
            CookieJar cookieJar;
            /** * 緩存類,內部使用了DiskLruCache來進行管理緩存,匹配緩存的機制不只僅是根據url, * 並且會根據請求方法和請求頭來驗證是否能夠響應緩存。此外,僅支持GET請求的緩存 */
            @Nullable Cache cache;
            //內置緩存
            @Nullable InternalCache internalCache;
            //Socket的抽象建立工廠,經過createSocket來建立Socket
            SocketFactory socketFactory;
            /** * 安全套接層工廠,HTTPS相關,用於建立SSLSocket。通常配置HTTPS證書信任問題都須要從這裏着手。 * 對於不受信任的證書通常會提示 * javax.net.ssl.SSLHandshakeException異常。 */
            @Nullable SSLSocketFactory sslSocketFactory;
            /** * 證書鏈清潔器,HTTPS相關,用於從[Java]的TLS API構建的原始數組中統計有效的證書鏈, * 而後清除跟TLS握手不相關的證書,提取可信任的證書以即可以受益於證書鎖機制。 */
            @Nullable CertificateChainCleaner certificateChainCleaner;
            /** * 主機名驗證器,與HTTPS中的SSL相關,當握手時若是URL的主機名 * 不是可識別的主機,就會要求進行主機名驗證 */
            HostnameVerifier hostnameVerifier;
            /** * 證書鎖,HTTPS相關,用於約束哪些證書能夠被信任,能夠防止一些已知或未知 * 的中間證書機構帶來的攻擊行爲。若是全部證書都不被信任將拋出SSLPeerUnverifiedException異常。 */
            CertificatePinner certificatePinner;
            /** * 身份認證器,當鏈接提示未受權時,能夠經過從新設置請求頭來響應一個 * 新的Request。狀態碼401表示遠程服務器請求受權,407表示代理服務器請求受權。 * 該認證器在須要時會被RetryAndFollowUpInterceptor觸發。 */
            Authenticator proxyAuthenticator;
            Authenticator authenticator;
            /** * 鏈接池 * * 咱們一般將一個客戶端和服務端和鏈接抽象爲一個 connection, * 而每個 connection 都會被存放在 connectionPool 中,由它進行統一的管理, * 例若有一個相同的 http 請求產生時,connection 就能夠獲得複用 */
            ConnectionPool connectionPool;
            //域名解析系統
            Dns dns;
            //是否遵循SSL重定向
            boolean followSslRedirects;
            //是否重定向
            boolean followRedirects;
            //失敗是否從新鏈接
            boolean retryOnConnectionFailure;
            //回調超時
            int callTimeout;
            //鏈接超時
            int connectTimeout;
            //讀取超時
            int readTimeout;
            //寫入超時
            int writeTimeout;
            //與WebSocket有關,爲了保持長鏈接,咱們必須間隔一段時間發送一個ping指令進行保活;
            int pingInterval;
    
            public Builder() {
                dispatcher = new Dispatcher();
    
                protocols = DEFAULT_PROTOCOLS;
    
                connectionSpecs = DEFAULT_CONNECTION_SPECS;
                eventListenerFactory = EventListener.factory(EventListener.NONE);
                /** * 代理選擇類,默認不使用代理,即便用直連方式,固然,咱們能夠自定義配置,以指定URI使用某種代理,相似代理軟件的PAC功能 */
                proxySelector = ProxySelector.getDefault();
                if (proxySelector == null) {
                    proxySelector = new NullProxySelector();
                }
                cookieJar = CookieJar.NO_COOKIES;
                socketFactory = SocketFactory.getDefault();
                hostnameVerifier = OkHostnameVerifier.INSTANCE;
                certificatePinner = CertificatePinner.DEFAULT;
                proxyAuthenticator = Authenticator.NONE;
                authenticator = Authenticator.NONE;
                connectionPool = new ConnectionPool();
                dns = Dns.SYSTEM;
                followSslRedirects = true;
                followRedirects = true;
                retryOnConnectionFailure = true;
                callTimeout = 0;
                connectTimeout = 10_000;
                readTimeout = 10_000;
                writeTimeout = 10_000;
                pingInterval = 0;
            }
          
          ....//省略部分代碼
    }
    複製代碼

    經過 OkHttpClient 的構造函數this(new Builder()); 看到這裏是否是很熟悉,沒錯就是咱們熟悉的建造者模式,那麼咱們也能夠經過以下鏈式調用進行一些參數配置,

    OkHttpClient build = new OkHttpClient.Builder().
                    addInterceptor().
                    addNetworkInterceptor().
                    writeTimeout().
                    build();
    複製代碼

    下面咱們接着看 this(new Builder()); 看到 Builder 中的註釋咱們得知,就是配置一大堆 OKHttp 屬性,你們想想若是這裏實現的是 new OKHttpClient("","","",n+ ...)有不少參數這樣是否是很繁瑣,其實你們若是在項目中碰見不少參數須要配置的話,也能夠進行 Builder 模式進行設計。

  2. 構建一個 Request 請求

    Request request = new Request.Builder()
                .url(url)
                .build();
    複製代碼

    能夠看到上面構建請求也是一個建造者模式,咱們看看 Request 類中作了什麼

    public final class Request {
      ...//部分代碼省略
      
      public static class Builder {
        //URL 管理
        @Nullable HttpUrl url;
        //請求類型,支持(GET,HEAD,POST,DELETE,PUT,PATCH)
        String method;
        //Http消息的頭字段
        Headers.Builder headers;
        //傳給服務器的 body 數據
        @Nullable RequestBody body;
    
    
        Map<Class<?>, Object> tags = Collections.emptyMap();
    
        public Builder() {
          this.method = "GET"; //默認 GET
          this.headers = new Headers.Builder();
        }
    
        Builder(Request request) {
          this.url = request.url;
          this.method = request.method;
          this.body = request.body;
          this.tags = request.tags.isEmpty()
              ? Collections.emptyMap()
              : new LinkedHashMap<>(request.tags);
          this.headers = request.headers.newBuilder();
        }
    
        public Builder url(HttpUrl url) {
          if (url == null) throw new NullPointerException("url == null");
          this.url = url;
          return this;
        }
      
      ....//省略部分代碼
        
    }
    複製代碼

    分析完 Request 咱們知道,就是封裝請求信息。

  3. 建立 RealCall

    okHttpClient.newCall(request)
    複製代碼
    @Override public Call newCall(Request request) {
        return RealCall.newRealCall(this, request, false /* for web socket */);
      }
    
      static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        // Safely publish the Call instance to the EventListener.
        RealCall call = new RealCall(client, originalRequest, forWebSocket);
        call.transmitter = new Transmitter(client, call);
        return call;
      }
    複製代碼

    能夠看到 newCall 根據 Request 會建立一個 Call 它的實現類也就是 RealCall。

  4. 同步執行

    okHttpClient.newCall(request).execute();
    複製代碼
    @Override public Response execute() throws IOException {
        synchronized (this) {
          //1. 是否已經執行過了
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        transmitter.timeoutEnter();
        transmitter.callStart();
        try {
          //2. 調用 OKHttpClient 的調度器分發給同步執行
          client.dispatcher().executed(this);
          //3. 調用攔截器
          return getResponseWithInterceptorChain();
        } finally {
          //不論是否執行成功都關閉當前請求任務
          client.dispatcher().finished(this);
        }
      }
    複製代碼

    經過 execute 函數內部實現,咱們知道

    1. 首先判斷這個請求是否已經執行了,若是已經執行了就拋出一個異常,很明顯這裏只容許執行一次。
    2. 調用器分發任務下去
    3. 調用攔截器進行處理請求,響應。

    咱們看註釋 2 代碼實現

    synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
      }
    複製代碼

    將當前執行請求的 call 添加進行同步隊列中,這裏出現了一個 runningSyncCalls 容器,咱們具體看下是什麼

    /** Ready async calls in the order they'll be run. */
      private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
    
      /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
      private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    
      /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
      private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
    複製代碼

    根據上面註釋可知:

    readyAsyncCalls:準備須要執行的異步任務

    runningAsyncCalls:正在執行的異步任務

    runningSyncCalls:正在執行的同步任務

    註釋 3 的代碼咱們待會跟講解異步源碼一塊兒看,由於它們最後都會調用 getResponseWithInterceptorChain 函數

  5. 異步處理

    okHttpClient.newCall(request).enqueue(new Callback() {
    
    @Override
    public void onFailure(Call call, IOException e) {
    	}
    
    @Override
    public void onResponse(Call call, Response response) throws IOException {
    
    		}
    });
    複製代碼

    由於 Call 的實現類是 RealCall ,因此咱們直接看 RealCall 的 enqueue

    @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));
      }
    複製代碼

    很明顯這裏跟同步執行的其實都大同小異,也都只容許執行一次,最後調用分發器進行 enqueue

  6. enqueue

    void enqueue(AsyncCall call) {
        synchronized (this) {
          //1. 
          readyAsyncCalls.add(call);
          //2. 
          if (!call.get().forWebSocket) {
            AsyncCall existingCall = findExistingCallWithHost(call.host());
            if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
          }
        }
        //3. 
        promoteAndExecute();
      }
    複製代碼

    根據註釋首先將要執行的異步任務加入準備執行的異步隊列中,而後判斷是不是 WebSocket 最後調用 promoteAndExecute 是要準備執行了,咱們看下它的實現:

    private int maxRequests = 64;
      private int maxRequestsPerHost = 5;
    
    private boolean promoteAndExecute() {
        assert (!Thread.holdsLock(this));
    
        List<AsyncCall> executableCalls = new ArrayList<>();
        boolean isRunning;
        synchronized (this) {
          //1. 
          for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
            AsyncCall asyncCall = i.next();
    				//2.
            if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
            //3. 
            if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
    				//4. 
            i.remove();
            asyncCall.callsPerHost().incrementAndGet();
            //5. 
            executableCalls.add(asyncCall);
            runningAsyncCalls.add(asyncCall);
          }
          isRunning = runningCallsCount() > 0;
        }
    		//6. 
        for (int i = 0, size = executableCalls.size(); i < size; i++) {
          AsyncCall asyncCall = executableCalls.get(i);
          asyncCall.executeOn(executorService());
        }
    
        return isRunning;
      }
    複製代碼

    根據上面代碼標註的註釋,咱們來分析下具體意思

    1. 首先準備執行的異步任務
    2. 判斷正在運行的異步任務數量是否已經操做最大限制 64 。
    3. 判斷請求同一個主機 host 不能大於 5(能夠經過Get與Set方式自定義設置)
    4. 先刪除當前須要準備執行的異步任務
    5. 將當前須要準備執行的異步任務加入到正在運行異步任務的隊列中
    6. 遍歷當前須要執行的異步任務,拿到異步任務執行。

    咱們來看下 asyncCall.executeOn(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;
      }
    複製代碼

    根據上面代碼能夠知道,返回的是一個執行線程池的對象,看下內部參數表明的意思,該方法是一個同步方法,ThreadPoolExecutor() 的核心線程數量爲 0 ,若是空閒的話是 60 s 的存活期,第二個參數傳入了 Integer 的最大值,即線程池所能容納的最大線程數爲 Integer.MAX_VALUE ,雖然這裏設置了很大的值,可是實際狀況下並不是會達到最大值,由於上面 enqueue() 方法中有作了判斷。

  7. 既然執行了子線程那麼確定會走 run 函數,咱們找一下

    final class AsyncCall extends NamedRunnable {
       ....//經過源碼查看 AsyncCall 內部沒有 run 函數,那麼咱們找它的父類
     }
    複製代碼
    public abstract class NamedRunnable implements Runnable {
      protected final String name;
    
      public NamedRunnable(String format, Object... args) {
        this.name = Util.format(format, args);
      }
    
      @Override public final void run() {
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(name);
        try {
          execute();
        } finally {
          Thread.currentThread().setName(oldName);
        }
      }
    
      protected abstract void execute();
    }
    複製代碼

    在父類找到了 run 函數,並執行了一個抽象函數 execute,咱們看它的子類 AsyncCall

    @Override protected void execute() {
          boolean signalledCallback = false;
          transmitter.timeoutEnter();
          try {
            //1. 
            Response response = getResponseWithInterceptorChain();
            signalledCallback = true;
            //2. 
            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 {
            //3. 
            client.dispatcher().finished(this);
          }
        }
    複製代碼

    經過上面代碼註釋分析得知:

    1. 經過 getResponseWithInterceptorChain(); 函數咱們能夠拿到服務器返回的響應數據
    2. 經過回調把響應數據返回給調用層
    3. 最後 finished,判斷是否有須要執行的任務,而後繼續刪除準備執行的異步任務,開始執行刪除的異步任務。

    到了這裏 同步執行異步執行 最後都調用getResponseWithInterceptorChain(); 函數,下面咱們來看下它的具體實現

  8. getResponseWithInterceptorChain();

    Response getResponseWithInterceptorChain() throws IOException {
      /** * 分類:一、應用攔截器 二、網絡攔截器 */
    	List<Interceptor> interceptors = new ArrayList();
      //用戶添加的全局攔截器
    	interceptors.addAll(this.client.interceptors());
      //錯誤、重定向攔截器
    	interceptors.add(new RetryAndFollowUpInterceptor(this.client));
      //橋接攔截器,橋接應用層與網絡層,添加必要的頭
    	interceptors.add(new BridgeInterceptor(this.client.cookieJar()));
      //緩存處理,Last-Modified、ETag、DiskLruCache等
    	interceptors.add(new CacheInterceptor(this.client.internalCache()));
      //鏈接攔截器
    	interceptors.add(new ConnectInterceptor(this.client));
    	if (!this.forWebSocket) {
       //經過okHttpClient.Builder#addNetworkInterceptor()傳進來的攔截器只對非網頁的請求生效
    		interceptors.addAll(this.client.networkInterceptors());
    	}
      //真正訪問服務器的攔截器
    	interceptors.add(new CallServerInterceptor(this.forWebSocket));
      //一個包裹這request的chain
    	Chain chain = new RealInterceptorChain(interceptors, this.transmitter, 		(Exchange)null, 0, this.originalRequest, this, 					this.client.connectTimeoutMillis(), this.client.readTimeoutMillis(), this.client.writeTimeoutMillis());
    	boolean calledNoMoreExchanges = false;
    	Response var5;
    	try {
        //開始執行攔截器
    		Response response = chain.proceed(this.originalRequest);
    		if (this.transmitter.isCanceled()) {
    			Util.closeQuietly(response);
           throw new IOException("Canceled");
                }
                var5 = response;
            } catch (IOException var9) {
                calledNoMoreExchanges = true;
                throw this.transmitter.noMoreExchanges(var9);
            } finally {
                if (!calledNoMoreExchanges) {
                    this.transmitter.noMoreExchanges((IOException)null);
                }
            }
    	return var5;
    }
    複製代碼

    上面代碼發現 new了一個 ArrayList ,而後就是不斷的 add,而後 new 了 RealInterceptorChain 對象,最後調用了chain.proceed() 方法。先看下RealInterceptorChain 的構造函數。

    public RealInterceptorChain(List<Interceptor> interceptors, Transmitter transmitter, @Nullable Exchange exchange, int index, Request request, Call call, int connectTimeout, int readTimeout, int writeTimeout) {
        this.interceptors = interceptors;
        this.transmitter = transmitter;
        this.exchange = exchange;
        this.index = index;
        this.request = request;
        this.call = call;
        this.connectTimeout = connectTimeout;
        this.readTimeout = readTimeout;
        this.writeTimeout = writeTimeout;
      }
    複製代碼

    構造函數裏面其實沒有作些什麼,就是一些賦值動做。最後看下 chain.proceed() 內部實現

    public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange) throws IOException {
        //若是當前 index 大於總的攔截器數量就拋異常
        if (index >= interceptors.size()) throw new AssertionError();
    		//每調用一次就自增長 1
        calls++;
    
        // 若是咱們已經有了一個流,請確認傳入的請求將複用它
        if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
          throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
              + " must retain the same host and port");
        }
    
        // 若是咱們已經有了一個流,請確認這是對chain.proceed()的惟一調用.
        if (this.exchange != null && calls > 1) {
          throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
              + " must call proceed() exactly once");
        }
    
        // 內部又建立一個 RealInterceptorChain 對下一個攔截器處理
        RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
            index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
        //拿到當前的攔截器
        Interceptor interceptor = interceptors.get(index);
        //執行下一個攔截器,返回 response
        Response response = interceptor.intercept(next);
    
        ....
    
        return response;
      }
    複製代碼

    根據上面代碼看到在 proceed 方面裏面又 new 了一個 RealInterceptorChain 類的 next 對象,這個 next 對象和 chain 最大的區別就是 index 屬性值不一樣 chain 是 0. 而 next 是1,而後取 interceptors 下標爲 1 的對象的 interceptor。由從上文可知,若是沒有開發者自定義的Interceptor 時,首先調用的 RetryAndFollowUpInterceptor,若是有開發者本身定義的interceptor 則調用開發者interceptor。

    這裏須要說明一下,由於攔截器在 OKHttp 設計中最爲重要或是最值得學習之一,咱們須要單獨寫一篇介紹攔截器,因此這裏咱們着重介紹執行流程

    後面的流程是在每個 interceptor 的 intercept 方法裏面都會調用 chain.proceed() 從而調用下一個 interceptorintercept(next) 方法,這樣就能夠實現遞歸遍歷getResponseWithInterceptorChain 裏面的 interceptors ,不知道這種調用方式你們有沒有熟悉感,是否是以爲跟 責任鏈模式很相像。沒錯,它其實就是設計模式當中的責任鏈模式。每條鏈處理的任務都是獨立的,也實現瞭解耦功能,它的強大徹底在這裏體現出來了。縮減後的責任鏈模式攔截器代碼以下:

    //RetryAndFollowUpInterceptor.java
    public Response intercept(Chain chain) throws IOException {
     //忽略部分代碼
     response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
     //忽略部分代碼
    }
    複製代碼
    //BridgeInterceptor.java
    public Response intercept(Chain chain) throws IOException {
      //忽略部分代碼
      Response networkResponse = chain.proceed(requestBuilder.build());
      //忽略部分代碼
    }
    複製代碼
    //CacheInterceptor.java
    public Response intercept(Chain chain) throws IOException {
       //忽略部分代碼
       networkResponse = chain.proceed(networkRequest);
       //忽略部分代碼
    }
    複製代碼
    //ConnectInterceptor.java
    public Response intercept(Chain chain) throws IOException {
         //忽略部分代碼
         return realChain.proceed(request, streamAllocation, httpCodec, connection);
    }
    複製代碼

    讀過源碼咱們知道 getResponseWithInterceptorChain裏面interceptors的最後一個 攔截器 是 CallServerInterceptor.java,最後一個 Interceptor (即CallServerInterceptor) 裏面是直接返回了response 而不是進行繼續遞歸,具體裏面是經過OKIO實現的,具體代碼,等後面再詳細說明,CallServerInterceptor返回response後, 返回給上一個 interceptor ,通常是開發者本身定義的 networkInterceptor ,而後開發者本身的 networkInterceptor 把他的 response 返回給前一個 interceptor ,依次類推返回給第一個 interceptor,這時候又回到了 realCall 裏面的 execute() 裏面了,代碼以下:

    @Override protected void execute() {
         ...
          try {
            Response response = getResponseWithInterceptorChain();
            signalledCallback = true;
            //將 response 返回給調用層
            responseCallback.onResponse(RealCall.this, response);
          } catch (IOException e) {
           .... //省略代碼
         
        }
    複製代碼

    最後經過 responseCallback.onResponse(RealCall.this, response); 回調返回給了調用層,至此,請求 -> 響應流程咱們分析完了,最後咱們來總結下

總結

同步請求流程:

  • 建立 OKHttpClient 實例,配置屬性
  • 構建 Request ,配置屬性
  • 構建 Realcall ,調用 executed
  • 執行Dispatcher.executed() 中的 runningSyncCalls 將 Realcall 添加到同步執行隊列中
  • 經過 getResponseWithInterceptorChain() 內部的責任鏈調用對 Request 層層處理包裝,最後在 CallServerInterceptor 攔截器中發起請求和得到 Response
  • 經過Dispatcher.finished(),把 call 實例從隊列中移除,並執行下一次任務。

異步請求流程:

  • 建立 OKHttpClient 實例,配置屬性

  • 構建 Request ,配置屬性

  • 構建 Realcall,調用 enqueue

  • 生成一個AsyncCall(responseCallback)實例(實現了Runnable)

  • AsyncCall實例放入了Dispatcher.enqueue()中,並判斷 maxRequests (最大請求數 64)maxRequestsPerHost(最大host請求數 5)是否知足條件,若是知足就把AsyncCall添加到runningAsyncCalls中,並放入線程池中執行;若是條件不知足,就添加到等待就緒的異步隊列,當那些知足的條件的執行時 ,在Dispatcher.finifshed(this)中的promoteCalls();方法中 對等待就緒的異步隊列進行遍歷,生成對應的AsyncCall實例,並添加到runningAsyncCalls中,最後放入到線程池中執行,一直到全部請求都結束。

參考

square OKHttp 官方網站

OKhttp 源碼分析系列

相關文章
相關標籤/搜索