OkHttp原理解析1(框架流程篇)

吊兒郎當發表於2020-02-26

OkHttp3.png
一直想寫一篇 簡潔而不失內涵 的OKHTTP原始碼分析,甚至從19年春節前就開始翻閱OkHttp的原始碼。但是趕上春節事多心雜,沒能將心中所想梳理出來。 現在疫情當前,節約了外出活動的時間,靜心打磨了此文,希望對看本文的小夥伴有所幫助,更希望 武漢堅強,祖國安康

本文原始碼基於OkHttp3.14.6,該版本是Java版最新的一版,後續的4.*全面使用了Kotlin,如有需要可再進行分析。

針對OkHttp我打算開兩篇進行分析。

第1篇分析整體的框架設計,以及大方向的請求流程。 第2篇分析具體攔截器的功能邏輯。

好,我們開始表演。

在翻看原始碼之前,對OkHttp的瞭解只停留在使用層面,對Api使用非常的6。完全沒關心過內部原理。更別提設計思想了。 有句 德瑪西亞 名言咋說來著 新手拼的是英雄,技能,熟練度。高玩靠的是走位,意識,英雄池。 這句話你品,你細品~~~

本文爭取以一個高玩的姿態去解讀OkHttp的原始碼,至於有多高呢? 嗯~~~ 還是這麼高。。。

image.png
我們再看原始碼的時候有一個技巧,不一定非要挨個類去逐行閱讀,枯燥無味不說,關鍵是很難和功能相對應,可根據使用方式切入,根據邏輯呼叫鏈,層層跟進。 本文呢又是一篇深入淺出的題材,還是那迷人的姿勢,還是那熟悉的套路。 從使用到原理,從架構到邏輯。 使用體現的是框架的能力,架構承載的是框架的靈魂。 我們將從以下幾個問題出發,循循漸進,層層深入。

    1. OkHttp有什麼優勢?為啥要分析的是OkHttp而不是其他框架?
    1. OkHttp的使用方式有哪些?
    1. OkHttp的架構是什麼樣的?
    1. OkHttp的內部是咋實現的?

#####1. OkHttp有什麼優勢?為啥要分析的是OkHttp而不是其他框架? 其實Android的網路框架有很多,比如Android-Async-Http,Volley,OkHttp,Retrofit,那為啥偏要分析OkHttp呢?因為它屌啊~~~ 我們先看一個這個對比圖就明白了。

對比分析 Android-Async-Http volley OkHttp Retrofit
技術分析 基於HttpClient 基於HttpUrlConnection 基於Socket, 和HttpUrlConnection同級但並不屬於HttpUrlConnection 基於Okhttp
優勢 自動智慧請求重試 ;持久化cookie儲存 1.支援圖片載入;網路請求的排序。
2.優先順序處理快取。
3.多級別取消請求。
4.生命週期控制,退出後自動取消請求。
5.可擴充性好;可支援HttpClient、HttpUrlConnection、和OkHttp
1.高效能Http請求庫。
2.支援SPDY,共享同一個Socket來處理同一個伺服器所有的請求.
3.支援http2.0、websocket;支援同步、非同步。
4.內部封裝了執行緒池、資料轉換、引數使用、錯誤處理等。
5.支援GZIP來減少資料流量。
6.基於NIO和OKio,效能更高。
1.Api牛逼
2.支援通過註解配置引數,url等資訊。
3.支援Gson,Jackson,Protobuf。
4.支援RxJava。
5.程式碼簡化;解耦徹底。
劣勢 Android5.0之後不推薦用HttpClient ,並且該框架通知維護。 Volley的Request和Response都是把資料放到byte陣列裡,不支援輸入輸出流,把資料放到陣列中,如果檔案多了,陣列就會大,消耗記憶體 ... ...

目前相對主流的框架Retrofit,Glide中都是內建了OkHttp,而Retrift自己即是網路框架,且它都基於OkHttp,可見OkHttp是怎樣一個地位。

#####2. OkHttp的使用方式有哪些? 接下來將秀8種操作,看好了您嘞。

  • 1.同步Get請求
  • 2.非同步Get請求
  • 3.同步Post請求
  • 4.非同步Post請求
  • 5.Post提交檔案
  • 6.Post提交from表單
  • 7.檔案字串複合提交
  • 8.攔截器使用

/**
  * 1.同步Get請求
  * 2.非同步Get請求
  * 3.同步Post請求
  * 4.非同步Post請求
  * 5.Post提交檔案
  * 6.Post提交from表單
  * 7.檔案字串複合提交
  * 8.攔截器使用
  */

  OkHttpClient okHttpClient = new OkHttpClient();

    /**
     * 同步Get請求
     */
    private void SyncGet() {
        try {
            Request request = new Request.Builder()
                    .url("url")
                    .build();
            Call call = okHttpClient.newCall(request);
            Response response = call.execute();
        } catch (Exception e) {

        }

    }

    /**
     * 非同步Gost請求
     */
    private void AsyncGet() {
        Request request = new Request.Builder()
                .url("url")
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //請求失敗
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //請求成功
            }
        });

    }

    /**
     * 同步Post請求
     */
    private void SyncPost() {
        try {
            MediaType JSON = MediaType.parse("application/x-www-form-urlencoded; charset=utf-8");
            String str = "通訊資料";
            Request request = new Request.Builder().url("url").post(RequestBody.create(JSON, str)).build();
            Call call = okHttpClient.newCall(request);
            Response response = call.execute();
        } catch (Exception e) {

        }

    }

    /**
     * 非同步Post請求
     */
    private void AsyncPost() {
        MediaType JSON = MediaType.parse("application/x-www-form-urlencoded; charset=utf-8");
        String str = "通訊資料";
        Request request = new Request.Builder().url("url").post(RequestBody.create(JSON, str)).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //請求失敗
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //請求成功
            }
        });
    }

    private void postFile() {
        MediaType fileMediaType = MediaType.parse("text/x-markdown; charset=utf-8");
        Request request = new Request.Builder()
                .url("url")
                .post(RequestBody.create(fileMediaType, new File("sd/mnt/a.png")))
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //請求失敗
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //請求成功
            }
        });
    }

    /**
     * Post提交from表單
     */
    private void postFrom() {
        MediaType fileMediaType = MediaType.parse("text/x-markdown; charset=utf-8");
        Request request = new Request.Builder()
                .url("url")
                .post(new FormBody.Builder().add("key", "value").build())
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //請求失敗
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //請求成功
            }
        });
    }

  /**
     * Post提交組合資料(字串+檔案)
     */
    private void postMultipartBody() {
        MediaType fileMediaType = MediaType.parse("image/png");
        RequestBody requestBody = RequestBody.create(fileMediaType, new File("sd/mnt/1.png"));
        MultipartBody multipartBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addPart(
                        Headers.of("Content-Disposition", "form-data; name=\"title\""),
                        RequestBody.create(null, "文字")//這樣可以直接新增資料,無需單獨建立RequestBody
                )
                .addPart(
                        Headers.of("Content-Disposition", "form-data; name=\"image\""),
                        RequestBody.create(fileMediaType, new File("sd/mnt/1.png"))//這樣可以直接新增檔案,無需單獨建立RequestBody
                )
                .addFormDataPart("key", "value")//新增表單資料
                .addFormDataPart("file", "fileName", requestBody)
                .build();
        Request request = new Request.Builder()
                .url("url")
                .post(multipartBody)
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //請求失敗
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //請求成功
            }
        });
    }


    /**
     * 使用攔截器
     */
    private void postAndInterceptor() {
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(new MyInterceptor())
                .build();
        MediaType fileMediaType = MediaType.parse("image/png");
        RequestBody requestBody = RequestBody.create(fileMediaType, new File("sd/mnt/1.png"));
        MultipartBody multipartBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("key", "value")//新增表單資料
                .addFormDataPart("file", "fileName", requestBody)
                .build();
        Request request = new Request.Builder()
                .url("url")
                .post(multipartBody)
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //請求失敗
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //請求成功
            }
        });
    }

    /**
     * 攔截器
     */
    public class MyInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Response response = chain.proceed(request);
            Log.d("TAG", "請求返回資料為:" + response.body().string());

            return null;
        }
    }
複製程式碼

#####3. OkHttp的架構是什麼樣的? 這個問題挺深入,唯有此圖以示天下。

整體架構圖.png
上圖主要以執行流程來劃分,其主要涉及的類包含以下幾個

  • OkHttpClient 客戶端物件

  • OkHttpClient.Builder 以構件者模式建立OkHttpClient物件

  • Request 請求物件

    • HttpUrl url; 請求物件引數
    • String method;請求物件引數
    • Headers.Builder headers;請求物件引數
    • RequestBody body;請求物件引數
    • Map<Class<?>, Object> tags = Collections.emptyMap();請求物件引數
  • Response 請求結果物件。

  • Request.Builder 構建者模式用於建立Request

  • RealCall implements Call 請求控制器,用於執行request

  • Dispatcher 排程器,主要用於請求的策略處理。

  • AsyncCall是一個Runnable,執行緒池呼叫。

  • Callback 接收回撥使用

  • Interceptor 攔截器介面,所有的攔截器均實現該介面,責任鏈模式的每一個攔截器介面。 #####4. OkHttp的內部是咋實現的? 又到了你麼最喜歡的講道理擺姿勢的環節了。 分析OkHttp的內部實現可以從使用角度出發,以使用邏輯分析框架入口,然後層層跟進。我們以普通的get請求為例,主要涉及以下幾個入口。

  • 1. OkHttpClient okHttpClient = new OkHttpClient();

  • 2. Request request = new Request.Builder().url("url").build();

  • 3. Call call = okHttpClient.newCall(request);

  • 4. call.enqueue(new Callback() {... });

我們逐個分析一下。 4.1. OkHttpClient okHttpClient = new OkHttpClient();內部執行了哪些邏輯?我們看下原始碼

//OkHttpClient的建構函式
public OkHttpClient() {
    this(new Builder());
  }
//OkHttpClient的建構函式
  OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.eventListenerFactory = builder.eventListenerFactory;
    this.proxySelector = builder.proxySelector;
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;

    boolean isTLS = false;
    for (ConnectionSpec spec : connectionSpecs) {
      isTLS = isTLS || spec.isTls();
    }

    if (builder.sslSocketFactory != null || !isTLS) {
      this.sslSocketFactory = builder.sslSocketFactory;
      this.certificateChainCleaner = builder.certificateChainCleaner;
    } else {
      X509TrustManager trustManager = Util.platformTrustManager();
      this.sslSocketFactory = newSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }

    if (sslSocketFactory != null) {
      Platform.get().configureSslSocketFactory(sslSocketFactory);
    }

    this.hostnameVerifier = builder.hostnameVerifier;
    this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
        certificateChainCleaner);
    this.proxyAuthenticator = builder.proxyAuthenticator;
    this.authenticator = builder.authenticator;
    this.connectionPool = builder.connectionPool;
    this.dns = builder.dns;
    this.followSslRedirects = builder.followSslRedirects;
    this.followRedirects = builder.followRedirects;
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
    this.callTimeout = builder.callTimeout;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval;

    if (interceptors.contains(null)) {
      throw new IllegalStateException("Null interceptor: " + interceptors);
    }
    if (networkInterceptors.contains(null)) {
      throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
    }
  }
    //OkHttpClient 中靜態內部類Builder的構造方法
    public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      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 中靜態內部類Builder的構造方法
    Builder(OkHttpClient okHttpClient) {
      this.dispatcher = okHttpClient.dispatcher;
      this.proxy = okHttpClient.proxy;
      this.protocols = okHttpClient.protocols;
      this.connectionSpecs = okHttpClient.connectionSpecs;
      this.interceptors.addAll(okHttpClient.interceptors);
      this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
      this.eventListenerFactory = okHttpClient.eventListenerFactory;
      this.proxySelector = okHttpClient.proxySelector;
      this.cookieJar = okHttpClient.cookieJar;
      this.internalCache = okHttpClient.internalCache;
      this.cache = okHttpClient.cache;
      this.socketFactory = okHttpClient.socketFactory;
      this.sslSocketFactory = okHttpClient.sslSocketFactory;
      this.certificateChainCleaner = okHttpClient.certificateChainCleaner;
      this.hostnameVerifier = okHttpClient.hostnameVerifier;
      this.certificatePinner = okHttpClient.certificatePinner;
      this.proxyAuthenticator = okHttpClient.proxyAuthenticator;
      this.authenticator = okHttpClient.authenticator;
      this.connectionPool = okHttpClient.connectionPool;
      this.dns = okHttpClient.dns;
      this.followSslRedirects = okHttpClient.followSslRedirects;
      this.followRedirects = okHttpClient.followRedirects;
      this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;
      this.callTimeout = okHttpClient.callTimeout;
      this.connectTimeout = okHttpClient.connectTimeout;
      this.readTimeout = okHttpClient.readTimeout;
      this.writeTimeout = okHttpClient.writeTimeout;
      this.pingInterval = okHttpClient.pingInterval;
    }
//通過靜態內部類Builder的build()方法可建立 OkHttpClient 物件。
 public OkHttpClient build() {
      return new OkHttpClient(this);
    }
複製程式碼

我們分析下這一大段程式碼,其實邏輯內容非常簡單,主要乾了這麼幾件事。

    1. 設定了快取、執行緒池、攔截器,超時等變數資料。
    1. OkHttpClient使用了構建者模式。
    1. new OkHttpClient()內部實際呼叫的是 new OkHttpClient(new Builder())。
    1. OkHttpClient還可通過new OkHttpClient.Builder().build();建立OkHttpClient物件。
  • 5.為什麼要使用構建者模式?當一個類的內部資料過於複雜的時候,要建立的話可能就需要了解這個類的內部結構以及相互關係等等。會大大提升框架的使用成本,因此建立的時候會有一個名為Builder的內部類别範本,設定好了預設的值和邏輯關係。這種模板可以有多個,使得同樣的構建過程可以建立不同的物件。使使用者不瞭解內部邏輯的情況下也可以正常建立物件。大大降低的使用成本。

4.2. Request request = new Request.Builder().url("url").build();內部又幹了啥? 從寫法上看,Request 一樣也使用了構建者模式,我們分開Request.Builder(),url("url"),build()一個一個看。

//Request建構函式
Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tags = Util.immutableMap(builder.tags);
  }
//Request的靜態內部類Builder建構函式
 public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }
  //Request的靜態內部類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();
    }
  //Request的靜態內部類Builder設定url方法
    public Builder url(HttpUrl url) {
      if (url == null) throw new NullPointerException("url == null");
      this.url = url;
      return this;
    }

    /**
     *  Request的靜態內部類Builder設定url方法
     *
     * @如果url是無效的則丟擲 throws IllegalArgumentException  通過呼叫HttpUrl.get(url)避免這種異常,
     * 無效的url返回null
     */
    public Builder url(String url) {
      if (url == null) throw new NullPointerException("url == null");

      // Silently replace web socket URLs with HTTP URLs.
      if (url.regionMatches(true, 0, "ws:", 0, 3)) {
        url = "http:" + url.substring(3);
      } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
        url = "https:" + url.substring(4);
      }

      return url(HttpUrl.get(url));
    }

    /**
     * Request的靜態內部類Builder設定url方法
     *
     * @throws IllegalArgumentException if the scheme of {@code url} is not {@code http} or {@code
     * https}.
     */
    public Builder url(URL url) {
      if (url == null) throw new NullPointerException("url == null");
      return url(HttpUrl.get(url.toString()));
    }

    // Request的靜態內部類Builder設定build方法
    public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }
複製程式碼

我們總結下 new Request.Builder().url("url").build()幹了幾件事。

    1. 通過new Request.Builder()構造者模式設定了預設的請求方式GET,且通過headers的構建者建立了 headers。
    1. url()方法使用到了物件導向過載的方法,入參支援HttpUrl 、String 、URL三種型別。最終目的即是設定請求地址。
    1. .build()則通過以上設定的屬性呼叫Request建構函式,建立Request物件。

4.3. Call call = okHttpClient.newCall(request);又是幹什麼的呢? 以上的4.1 , 4.2均是為了建立okHttpClient,Request物件,以及初始化一資料,並沒有進行其他操作。 okHttpClient.newCall(request);又做了什麼操作呢?

 /**
   * okHttpClient的newCall()
   * 準備request,將在某個時間執行。 
   */
  @Override public Call newCall(Request request) {
     //最後這個引數是否為web socket預設傳false
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }
 /**
   *RealCall.newRealCall(...)
   *例項化RealCall物件,初始化RealCall.transmitter。
   */
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;
  }
 /**
   *RealCall(...)
   */
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
  }
 /**
   *Transmitter類建構函式 。
   */
public Transmitter(OkHttpClient client, Call call) {
    this.client = client;
    this.connectionPool = Internal.instance.realConnectionPool(client.connectionPool());
    this.call = call;
    this.eventListener = client.eventListenerFactory().create(call);
    this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
  }
複製程式碼

總結:這一堆居然還是在建立物件,真正的請求還沒有開始。

    1. 通過okHttpClient.newCall()呼叫了RealCall.newRealCall()並把Request,okHttpClient作為引數傳過去。
    1. RealCall.newRealCall()中通過建構函式建立了RealCall和Transmitter物件。
  • 3.Transmitter主要用於設定連線池,事件監聽器,以及超時等待時間。
    • 3.1. 注意Transmitter中這句 Internal.instance.realConnectionPool(client.connectionPool()); Internal.instance只在OkHttpClient有實現,因此也只會呼叫OkHttpClient中的實現介面,目的是拿到connectionPool連線池。
    • 3.2. client.eventListenerFactory()獲取的EventListener.Factory其實是在OkHttpClient的Builder()中通過 EventListener.factory(EventListener.NONE);建立的。預設傳的EventListener是一個空的。

4.4. call.enqueue(new Callback() {... });應該開始請求了吧... 前邊準備的所有準備均為了最後這一步請求,我們看下邏輯是怎麼操作的。

@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));
  }
//transmitter.callStart();
public void callStart() {
    this.callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
    eventListener.callStart(call);
  }

複製程式碼

這塊有點複雜我們一點一點的分析,最後進行總結。 首先call.enqueue()中進行了執行狀態賦值,防止重複執行請求。 其次呼叫了transmitter.callStart();啟動堆疊的跟蹤,以及eventListener的一些回撥處理。 最後即是最關鍵的非同步 client.dispatcher().enqueue(new AsyncCall(responseCallback));是真正的請求流程。 我繼續深入分析

//Dispatcher.enqueue()
void enqueue(AsyncCall call) {
    synchronized (this) {
      //readyAsyncCalls是一個準備呼叫的AsyncCall佇列
      readyAsyncCalls.add(call);

      // 如果不是WebSocket,將通過call.host查詢是否有執行中的AsyncCall ,如果有將通過AtomicInteger型別共享到當前 AsyncCall 物件中。
      if (!call.get().forWebSocket) {
        AsyncCall existingCall = findExistingCallWithHost(call.host());
        //非同步請求,OkHttp會對有相同主機的call在請求時進行記數,通過AtomicInteger物件進行即時同步。
        //這個計數對後續的請求有影響,我們後邊再詳細分析。
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
      }
    }
    //真正執行操作
    promoteAndExecute();
  }
//迴圈runningAsyncCalls和readyAsyncCalls佇列
  @Nullable private AsyncCall findExistingCallWithHost(String host) {
    for (AsyncCall existingCall : runningAsyncCalls) {
      if (existingCall.host().equals(host)) return existingCall;
    }
    for (AsyncCall existingCall : readyAsyncCalls) {
      if (existingCall.host().equals(host)) return existingCall;
    }
    return null;
  }
/**
   *readyAsyncCalls中滿足條件的物件移動到runningAsyncCalls中 並且在 executor service上執行。 
   * 必須同步呼叫,因為要回撥使用者的程式碼
   *
   * 如果排程程式當前正在執行,則返回true
   */
  private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      //迴圈readyAsyncCalls佇列。
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();
        //如果執行佇列runningAsyncCalls超過了maxRequests直接break。(預設值為64)
        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        //如果當前的主機計數器>5則continue,這個計數器就是上述enqueue()方法的計數器。
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
        從readyAsyncCalls中移除
        i.remove();
        //相同主機的asyncCall計數器+1
        asyncCall.callsPerHost().incrementAndGet();
        executableCalls.add(asyncCall);
        //新增到runningAsyncCalls佇列中
        runningAsyncCalls.add(asyncCall);
      }
      //執行狀態賦值,非同步/同步只要有一個在執行則為true。
      isRunning = runningCallsCount() > 0;
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
     //開始執行,具體的執行邏輯我們後續分析。
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }
複製程式碼

ok,我們總結下Dispatcher.enqueue()幹了什麼事

    1. 將AsyncCall新增到準備佇列readyAsyncCalls。
    1. 通過AsyncCall物件查詢runningAsyncCalls和readyAsyncCalls佇列中是否想相同主機的AsyncCall,如果有則通過AtomicInteger物件將AsyncCall.callsPerHost引用到一起,方便後續的計數統計。
    1. 呼叫promoteAndExecute()迴圈readyAsyncCalls佇列,判斷runningAsyncCalls佇列大於64或asyncCall.callsPerHost>5均終止新增到runningAsyncCalls佇列中。
    1. asyncCall.callsPerHost計數器+1,AsyncCall新增到runningAsyncCalls佇列中,從readyAsyncCalls中移除。啟動for迴圈呼叫 asyncCall.executeOn(executorService());進行請求。

那**asyncCall.executeOn(executorService());**中又幹了啥?

//Dispatcher.executorService()
//如果executorService 為null則建立一個ThreadPoolExecutor執行緒池
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;
  }

/**
    * AsyncCall.executeOn()
     * 嘗試通過 executorService啟動執行AsyncCall
     * 如果 executor已經關閉了則嘗試清楚,並報失敗
     */
    void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        //執行緒池執行AsyncCall
        executorService.execute(this);
        success = true;
      } catch (RejectedExecutionException e) {
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        transmitter.noMoreExchanges(ioException);
        //回撥onFailure
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
          //如果沒成功,呼叫OkHttpClient.dispatcher().finished(this);
          client.dispatcher().finished(this); // This call is no longer running!
        }
      }
    }

/**
  * Dispatcher.finished()
  *失敗
  */
  void finished(AsyncCall call) {
    //AsyncCall 中相同主機計數器-1。
    call.callsPerHost().decrementAndGet();
    finished(runningAsyncCalls, call);
  }
//Dispatcher.finished(...)
private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
      //從runningAsyncCalls中移除call。
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      idleCallback = this.idleCallback;
    }
    //重新呼叫promoteAndExecute()
    boolean isRunning = promoteAndExecute();

    if (!isRunning && idleCallback != null) {
      //如果同步非同步均沒有執行了並且idleCallback 不為空 則通知當前為空閒狀態了。
      idleCallback.run();
    }
  }
複製程式碼

我們再總結下asyncCall.executeOn(executorService())到底幹了啥。

    1. 通過executorService()建立了一個ThreadPoolExecutor執行緒池。
    1. 啟動了executorService執行緒池。
    1. 如果執行緒池出現了異常則啟動異常處理,通過 responseCallback回撥onFailure
    1. 通過finally 關鍵字判斷如果本次請求失敗了則呼叫OkHttpClient.dispatcher().finished(this)
    • 4.1. AsyncCall.callsPerHost 相同主機計數器-1。
    • 4.2 . 從runningAsyncCalls中移除當前AsyncCall並重新呼叫promoteAndExecute()。
    • 4.3 . 如果同步非同步均沒有執行了並且idleCallback 不為空 則通知當前為空閒狀態了。
    1. executorService.execute(this);證明其實this為AsyncCall,型別為Runnable。

executorService.execute(this);是執行請求的邏輯內容,我們詳細看下 executorService是一個執行緒池,而this代表的是AsyncCall這個類,現在其實可以發現AsyncCall實際上繼承的是NamedRunnable,而NamedRunnable實現了Runnable,這下就簡單了,我們直接找Runnable的run方法即可。

public abstract class NamedRunnable implements Runnable {
...
  @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();
}

//AsyncCall.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);
        }
      } catch (Throwable t) {
        cancel();
        if (!signalledCallback) {
          IOException canceledException = new IOException("canceled due to " + t);
          canceledException.addSuppressed(t);
          responseCallback.onFailure(RealCall.this, canceledException);
        }
        throw t;
      } finally {
        //移除當前AsyncCall
        client.dispatcher().finished(this);
      }
    }
複製程式碼

我們觀察以上程式碼大部分是異常處理的邏輯,而try中第一句即返回了Response物件,因此,我們主要看getResponseWithInterceptorChain()中的邏輯。

  Response getResponseWithInterceptorChain() throws IOException {
    // 建立一個完整的攔截器堆疊
    List<Interceptor> interceptors = new ArrayList<>();
     //將建立okhttpclient時的攔截器新增到interceptors
    interceptors.addAll(client.interceptors());
    //重試攔截器,負責處理失敗後的重試與重定向
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    //請求轉化攔截器(使用者請求轉為伺服器請求,伺服器響應轉為使用者響應)
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //快取攔截器。負責
    //1.根據條件,快取配置,有效期等返回快取響應,也可增加到快取。
    //2.設定請求頭(If-None-Match、If-Modified-Since等) 伺服器可能返回304(未修改)
    //3.可配置自定義的快取攔截器。
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //網路連線攔截器,主要負責和伺服器建立連線。
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      //建立okhttpclient時設定的networkInterceptors
      interceptors.addAll(client.networkInterceptors());
    }
    //資料流攔截器,主要負責像伺服器傳送和讀取資料,請求報文封裝和解析。
    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 {
      //啟動責任鏈
      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);
      }
    }
  }
複製程式碼

其實上述邏輯中,主要乾了3件事。

    1. 將所有攔截器新增到了list中
    1. 通過RealInterceptorChain建立責任鏈。
    1. 通過chain.proceed(originalRequest)啟動執行。

我們只要搞懂建立責任鏈的邏輯,以及啟動責任鏈的邏輯就全都明白了,ok,我們看下具體程式碼

//通過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;
  }
//啟動責任鏈程式碼
 @Override 
 public Response proceed(Request request) throws IOException {
    return proceed(request, transmitter, exchange);
  }
//啟動責任鏈程式碼
  public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
      throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    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");
    }

    // 如果我們已經有一個流,確認這是對鏈的唯一呼叫。
    if (this.exchange != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // 呼叫鏈中的下一個攔截器,注意引數index+1,通過+1的方式迴圈interceptors list中的攔截器
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
        index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    //執行當前攔截器邏輯,並設定下一個攔截器物件。
    Response response = interceptor.intercept(next);

    // 確認下一個攔截器對chain.proceed()進行了所需的呼叫。
    if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    // Confirm that the intercepted response isn't null.
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    if (response.body() == null) {
      throw new IllegalStateException(
          "interceptor " + interceptor + " returned a response with no body");
    }

    return response;
  }
複製程式碼

雖然看著上述這段程式碼很長,但是大篇幅都在判斷各種異常情況,實際做的事非常簡單。 1.建立完RealInterceptorChain後,通過procee()判斷各種異常,並獲取當前Interceptor物件。 2.通過Interceptor.intercept(RealInterceptorChain)啟動當前攔截器邏輯,並且觸發下一個攔截器啟動。 3.如果當前攔截器出現異常等錯誤,則終止責任鏈。

具體的情況我們還需要看一個攔截器內部的邏輯,我們以簡單的ConnectInterceptor為例。

public final class ConnectInterceptor implements Interceptor {
  public final OkHttpClient client;

  public ConnectInterceptor(OkHttpClient client) {
    this.client = client;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    //chain實際上是下一個責任物件。
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    Transmitter transmitter = realChain.transmitter();

    // 我們需要網路來滿足這個要求。可能用於驗證條件GET。
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);
    //執行下一個攔截器責任物件的proceed方法。
    return realChain.proceed(request, transmitter, exchange);
  }
}
複製程式碼

可見,Interceptor.intercept(Chain chain)接收的是下一個攔截器責任物件。 該方法中執行了自己攔截器該有的邏輯,如果沒異常則直接通過下一個攔截器責任物件的proceed()啟動了下一個攔截器邏輯。 ok我們在回頭看下,AsyncCall.execute()中的finally中的那句程式碼 client.dispatcher().finished(this);

//Dispatcher.finished()
 void finished(AsyncCall call) {
    call.callsPerHost().decrementAndGet();
    finished(runningAsyncCalls, call);
  }

  private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
      //移除佇列,如果失敗則拋異常
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      idleCallback = this.idleCallback;
    }
    //這個方法是不是有點眼熟,主要用來將readyAsyncCalls符合條件的新增到runningAsyncCalls中,並執行。
    boolean isRunning = promoteAndExecute();
     //閒置呼叫
    if (!isRunning && idleCallback != null) {
      idleCallback.run();
    }
  }
複製程式碼

ok至此所有的流程都非常清楚了。至於請求細節需要具體到了每一個攔截器裡。我打算單獨開一篇來分析。 看到此處,如果對你有一點幫助,麻煩給個贊鼓勵一下。

其實寫部落格是一個自驅的學習管理方式。能給小夥伴說明白的,那你必定早已心中有數。翻閱偉大的框架程式碼就好比面對面跟這個偉人學習。

相關文章