抽絲剝繭okhttp(四)OkHttpClient原理

接上篇抽絲剝繭okhttp(三) http://www.reibang.com/p/cf59397dce1f
下面是極簡版的okhttp請求網(wǎng)絡(luò)的流程圖娶桦,之前分析過了Response、Request這兩個涉及http本身的協(xié)議的封裝汁汗。那么如何宏觀上看整個請求流程呢衷畦?
1 OkHttpClient負(fù)責(zé)把這些部件和配置裝配起來
2 用Call對象發(fā)出請求,
3 在發(fā)送和接收過程中通過很多攔截器處理知牌,
4 之后我們接得了response對象祈争,整個網(wǎng)絡(luò)請求流程完成。
一共四步角寸,聯(lián)想實際的網(wǎng)絡(luò)請求不難理解菩混。

image.png

那么這四步具體是如何實現(xiàn)的呢球匕?我們一步一步的來抽絲剝繭。

OkhttpClient

OkhttpClient充當(dāng)?shù)氖且粋€客戶端的角色照卦,負(fù)責(zé)收集請求役耕,配置信息,創(chuàng)建用于實際發(fā)出請求的Call。下面根據(jù)我們實際項目順序了解一下OkhttpClient的運行機(jī)制津辩。

1 .OkhttpClient的創(chuàng)建

public OkHttpClient() {
    this(new Builder());
  }

  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 = systemDefaultTrustManager();
      this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }

    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.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的創(chuàng)建也builder模式蚜印,但暴露給外部的是一new出來的窄赋,內(nèi)部調(diào)用第二個采用builder構(gòu)造方法柒傻。這樣做是為了初始化builder中默認(rèn)的重要參數(shù)红符,避免用戶調(diào)用的時候這么多的參數(shù)用戶很大可能上會配錯一些東西。這樣簡化了操作致开。

我們?nèi)绾伟l(fā)起請求,獲得response呢

 Request request = new Request.Builder()
      .url(url)
      .build();

  Response response = client.newCall(request).execute();

OkhttpClient實現(xiàn)了Call.Factory的newCall()虹蒋,這樣為我們生產(chǎn)出來一個新的隨時可以發(fā)起網(wǎng)絡(luò)請求的Call對象;

/**
   * Prepares the {@code request} to be executed at some point in the future.
   */
@Override 
public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

可以看到他返回的是一個用RealCall從創(chuàng)建出來的而且是RealCall對象晃虫。

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.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

這個RealCall對象正是對網(wǎng)絡(luò)請求的真正發(fā)起者哲银,起重大作用。RealCall實現(xiàn)了Call接口草巡,我們看Call的定義。

public interface Call extends Cloneable {
//返回原始的Request
  Request request();

//立即執(zhí)行網(wǎng)絡(luò)請求
  Response execute() throws IOException;

 //在將來的某一時刻發(fā)起請求
  void enqueue(Callback responseCallback);

//判斷是否被執(zhí)行了
  boolean isExecuted();
//判斷是否被取消了
  boolean isCanceled();

//創(chuàng)建出來一個新的Call對象
  Call clone();

  interface Factory {
    Call newCall(Request request);
  }
}

RealCall中復(fù)寫的這些方法全權(quán)的執(zhí)行了網(wǎng)絡(luò)請求的工作。所以我們繞了這么大彎子終于看到在哪進(jìn)行的網(wǎng)絡(luò)請求了棚亩。


  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }
@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

execute 和 enqueue中分別調(diào)用了
client.dispatcher().finished(this);;和 client.dispatcher().enqueue(new AsyncCall(responseCallback));
一個同步一個異步的纺阔。用dispatcher放到一個隊列中進(jìn)行分發(fā)運行质况,對于enqueue因為AsyncCall實現(xiàn)了Runnable接口结榄,在dispatcher中的的線程池運行隊列里的Runnable對象邻寿,執(zhí)行executorService().execute(call);所以最后的網(wǎng)絡(luò)請求還在這個AsyncCall對象對于runnable的實現(xiàn)中。他們共同的會調(diào)用

 Response response = getResponseWithInterceptorChain();

獲得了Response對象枝秤。那么getResponseWithInterceptorChain();又做了什么淀弹。

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

這里添加了okhttp默認(rèn)的幾個攔截器沐序,并進(jìn)入chain.proceed(originalRequest);至此進(jìn)入了一大堆攔截器各種攔截最后返回response的模式策幼,關(guān)于攔截器我們以后再說,現(xiàn)在先過唐含。經(jīng)過層層攔截器我們終于拿到了最終的響應(yīng)對象。如此推演我們可以推斷出我們真正的請求應(yīng)在最后一個攔截器中淮捆。我們看一下:就是那個CallServerInterceptor

/** This is the last interceptor in the chain. It makes a network call to the server. */
public final class CallServerInterceptor implements Interceptor {
  private final boolean forWebSocket;

  public CallServerInterceptor(boolean forWebSocket) {
    this.forWebSocket = forWebSocket;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    HttpCodec httpCodec = realChain.httpStream();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    RealConnection connection = (RealConnection) realChain.connection();
    Request request = realChain.request();

    long sentRequestMillis = System.currentTimeMillis();

    realChain.eventListener().requestHeadersStart(realChain.call());
    httpCodec.writeRequestHeaders(request);
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
      // Continue" response before transmitting the request body. If we don't get that, return
      // what we did get (such as a 4xx response) without ever transmitting the request body.
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        httpCodec.flushRequest();
        realChain.eventListener().responseHeadersStart(realChain.call());
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        realChain.eventListener().requestBodyStart(realChain.call());
        long contentLength = request.body().contentLength();
        CountingSink requestBodyOut =
            new CountingSink(httpCodec.createRequestBody(request, contentLength));
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);

        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
        realChain.eventListener()
            .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
      } else if (!connection.isMultiplexed()) {
        // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
        // from being reused. Otherwise we're still obligated to transmit the request body to
        // leave the connection in a consistent state.
        streamAllocation.noNewStreams();
      }
    }

    httpCodec.finishRequest();

    if (responseBuilder == null) {
      realChain.eventListener().responseHeadersStart(realChain.call());
      responseBuilder = httpCodec.readResponseHeaders(false);
    }

    Response response = responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    int code = response.code();
    if (code == 100) {
      // server sent a 100-continue even though we did not request one.
      // try again to read the actual response
      responseBuilder = httpCodec.readResponseHeaders(false);

      response = responseBuilder
              .request(request)
              .handshake(streamAllocation.connection().handshake())
              .sentRequestAtMillis(sentRequestMillis)
              .receivedResponseAtMillis(System.currentTimeMillis())
              .build();

      code = response.code();
    }

    realChain.eventListener()
            .responseHeadersEnd(realChain.call(), response);

    if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response = response.newBuilder()
          .body(Util.EMPTY_RESPONSE)
          .build();
    } else {
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }

    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      streamAllocation.noNewStreams();
    }

    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;
  }

  static final class CountingSink extends ForwardingSink {
    long successfulCount;

    CountingSink(Sink delegate) {
      super(delegate);
    }

    @Override public void write(Buffer source, long byteCount) throws IOException {
      super.write(source, byteCount);
      successfulCount += byteCount;
    }
  }
}

果不其然 我們看眼注釋就知道了:This is the last interceptor in the chain. It makes a network call to the server. */
也驗證了我們之前的猜測涩笤。intercept方法中確確實實的執(zhí)行了網(wǎng)絡(luò)請求,但在okhttp3.internal包中的類涉及底層更多一些恩沽,本人目前精力有限罗心,只能分析到這層,但目前看來對于輪子我們了解了差不多了飒箭,大件都拆了,小件拆了也不一定能用的到盈匾,所以就到這層削饵。
這里返回的 response就是我最終可以使用的response了窿撬。

這個response我們一層一層返回上層 我們在哪能拿到還記得么?那就是execute()同步請求中直接返回严里,enqueue中的callback中

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }


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


public interface Callback {
  /**
   * Called when the request could not be executed due to cancellation, a connectivity problem or
   * timeout. Because networks can fail during an exchange, it is possible that the remote server
   * accepted the request before the failure.
   */
  void onFailure(Call call, IOException e);

  /**
   * Called when the HTTP response was successfully returned by the remote server. The callback may
   * proceed to read the response body with {@link Response#body}. The response is still live until
   * its response body is {@linkplain ResponseBody closed}. The recipient of the callback may
   * consume the response body on another thread.
   *
   * <p>Note that transport-layer success (receiving a HTTP response code, headers and body) does
   * not necessarily indicate application-layer success: {@code response} may still indicate an
   * unhappy HTTP response code like 404 or 500.
   */
  void onResponse(Call call, Response response) throws IOException;
}


這樣開發(fā)者朋友們就拿到可請求之后的Response了迷帜。這樣我們終于看到okhttp官網(wǎng)上的那幾段示例代碼都做了什么事情了;我順便貼出來锦针,順便也回味一下:

Examples

GET A URL

This program downloads a URL and print its contents as a string. Full source.

OkHttpClient client = new OkHttpClient();

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

  Response response = client.newCall(request).execute();
  return response.body().string();
}

POST TO A SERVER

This program posts data to a service. Full source.

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

OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
  RequestBody body = RequestBody.create(JSON, json);
  Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
  Response response = client.newCall(request).execute();
  return response.body().string();
}

總結(jié)

總結(jié)一下盾碗,整個網(wǎng)絡(luò)請求過程中okhttpclient的角色就是準(zhǔn)備材料耗美,Call對象負(fù)責(zé)發(fā)起,但發(fā)起的意義在于把請求放到隊列里執(zhí)行蛇摸,在此之后一直到最后請求成功這中間又精力了若干的攔截器。正是這些攔截器實現(xiàn)了更多更具有拓展性的工作抠藕,默認(rèn)添加進(jìn)去的攔截器如下:

 List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

從字面意義上來看包含了眾多功能敬辣,而且我們后期還可以自己的定制自己的interceptor添加到這里面實現(xiàn)各式各樣的功能需求村刨,所以interceptor的設(shè)計師okhttp的絕妙之筆琅关。關(guān)于interceptor我們后續(xù)有機(jī)會再行解析。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诡必,一起剝皮案震驚了整個濱河市爸舒,隨后出現(xiàn)的幾起案子扭勉,更是在濱河造成了極大的恐慌,老刑警劉巖苛聘,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涂炎,死亡現(xiàn)場離奇詭異,居然都是意外死亡设哗,警方通過查閱死者的電腦和手機(jī)唱捣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門拣宰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來重贺,“玉大人潜圃,你說我怎么就攤上這事踏志≡惭悖” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我住拭,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮埃脏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己团滥,他們只是感情好力惯,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布茎匠。 她就那樣靜靜地躺著汽馋,像睡著了一般众眨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼霎桅。 笑死锻霎,一個胖子當(dāng)著我的面吹牛量窘,可吹牛的內(nèi)容都是我干的审葬。 我是一名探鬼主播鸦难,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼步淹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤止潮,失蹤者是張志新(化名)和其女友劉穎舆瘪,沒想到半個月后片效,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡英古,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年淀衣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片召调。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡膨桥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出唠叛,到底是詐尸還是另有隱情只嚣,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布玻墅,位于F島的核電站介牙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏澳厢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一囚似、第九天 我趴在偏房一處隱蔽的房頂上張望剩拢。 院中可真熱鬧,春花似錦饶唤、人聲如沸徐伐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽办素。三九已至角雷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間性穿,已是汗流浹背勺三。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留需曾,地道東北人吗坚。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像呆万,于是被迫代替她去往敵國和親商源。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • OkHttp源碼的samples的簡單使用的示例: public static void main(String....
    _warren閱讀 747評論 0 1
  • OkHttp解析系列 OkHttp解析(一)從用法看清原理OkHttp解析(二)網(wǎng)絡(luò)連接OkHttp解析(三)關(guān)于...
    Hohohong閱讀 20,974評論 4 58
  • 用OkHttp很久了谋减,也看了很多人寫的源碼分析牡彻,在這里結(jié)合自己的感悟,記錄一下對OkHttp源碼理解的幾點心得出爹。 ...
    藍(lán)灰_q閱讀 4,274評論 4 34
  • 這篇文章主要講 Android 網(wǎng)絡(luò)請求時所使用到的各個請求庫的關(guān)系庄吼,以及 OkHttp3 的介紹。(如理解有誤以政,...
    小莊bb閱讀 1,159評論 0 4
  • 完成: 1現(xiàn)實電視劇“人民的名義”及原小說 2言情小說“慕陽”還有一本不記得名字了霸褒,休閑看的,故事不是很新奇盈蛮,邏輯...
    星辰海綿oO閱讀 214評論 0 0