OkHttp3 -- 源碼

以get請(qǐng)求為例子:

  OkHttpClient client=new OkHttpClient();
  Request request=new Request.Builder().url("").build();
  Response response=client.newCall(request).execute();

三行代碼就可以創(chuàng)建一個(gè)請(qǐng)求 , 接下來(lái)我們查看具體的代碼

OkHttpClient client=new OkHttpClient();

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

構(gòu)造方法中再次調(diào)用有參的構(gòu)造方法 , 點(diǎn)進(jìn)去之后 , 會(huì)發(fā)現(xiàn)一系列的初始化操作 . 其中我們關(guān)心這幾個(gè)屬性 , 先記住名字就好

  • dispatcher 分發(fā)器
  • interceptors 應(yīng)用層的攔截器
  • networkInterceptors 網(wǎng)絡(luò)層攔截器

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

其實(shí)這里就是一個(gè)對(duì)象的初始化過(guò)程.......不多解釋了

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

從newCall()方法開(kāi)始:

@Override public Call newCall(Request request) {
    return new RealCall(this, request);
  }

直接實(shí)例化了另一個(gè)類(lèi)RealCall, 實(shí)例化之后 , 回到原來(lái)的語(yǔ)句中 , 接下來(lái)執(zhí)行的execute()方法 , 就肯定是在該類(lèi)中進(jìn)行的了
這里不要直接點(diǎn)擊execute , 會(huì)跑偏的= =

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    try {
      // 使用分發(fā)器來(lái)處理
      client.dispatcher().executed(this);
      // 交由攔截器來(lái)處理
      Response result = getResponseWithInterceptorChain(false);
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }

這里主要有兩個(gè)方法 , 一個(gè)分發(fā) , 一個(gè)攔截. 我們先看分發(fā)器的executed的實(shí)現(xiàn):

synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

恩.......比較簡(jiǎn)單昂 , 就是將請(qǐng)求添加到隊(duì)列中 , 這里用到隊(duì)列數(shù)據(jù)結(jié)構(gòu). 加入隊(duì)列之后, 攔截器來(lái)進(jìn)一步處理

private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
    Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
    return chain.proceed(originalRequest);
  }

首先創(chuàng)建了一個(gè)應(yīng)用層的攔截器ApplicationInterceptorChain , 再調(diào)用攔截器的proceed方法 , 獲取到請(qǐng)求數(shù)據(jù).
先點(diǎn)開(kāi)ApplicationInterceptorChain , 發(fā)現(xiàn)它是一個(gè)內(nèi)部類(lèi) , 初始化了一些東西 , 然后接下來(lái)的proceed方法也應(yīng)該在這個(gè)類(lèi)里面了

@Override public Response proceed(Request request) throws IOException {
      // If there's another interceptor in the chain, call that.
      if (index < client.interceptors().size()) {
        Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
        Interceptor interceptor = client.interceptors().get(index);
        Response interceptedResponse = interceptor.intercept(chain);

        if (interceptedResponse == null) {
          throw new NullPointerException("application interceptor " + interceptor
              + " returned null");
        }

        return interceptedResponse;
      }

      // No more interceptors. Do HTTP.
      // 開(kāi)始請(qǐng)求
      return getResponse(request, forWebSocket);
    }
  }

這里先判斷了當(dāng)前攔截器有沒(méi)有對(duì)應(yīng)的處理 , 沒(méi)有就新建一個(gè)攔截器 , 并執(zhí)行intercept方法 . 攔截器可以用來(lái)轉(zhuǎn)換格式 , 重寫(xiě)請(qǐng)求等......最后 , 反回了真正的請(qǐng)求getResponse()

  Response getResponse(Request request, boolean forWebSocket) throws IOException {
    // Copy body metadata to the appropriate request headers.
    RequestBody body = request.body();

    // 拼接請(qǐng)求頭
    if (body != null) {
      Request.Builder requestBuilder = request.newBuilder();

      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }

      request = requestBuilder.build();
    }

    // Create the initial HTTP engine. Retries and redirects need new engine for each attempt.
    // 創(chuàng)建HTTP engine, 用于發(fā)送和回復(fù)的細(xì)節(jié)處理
    engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);

    int followUpCount = 0;
    while (true) {
      if (canceled) {
        engine.releaseStreamAllocation();
        throw new IOException("Canceled");
      }

      boolean releaseConnection = true;
      try {
        // 發(fā)送請(qǐng)求
        engine.sendRequest();

        // 讀取回復(fù)
        engine.readResponse();
        releaseConnection = false;
      } catch (RequestException e) {
        // The attempt to interpret the request failed. Give up.
        throw e.getCause();
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        HttpEngine retryEngine = engine.recover(e.getLastConnectException(), true, null);
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }
        // Give up; recovery is not possible.
        throw e.getLastConnectException();
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        HttpEngine retryEngine = engine.recover(e, false, null);
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }

        // Give up; recovery is not possible.
        throw e;
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          StreamAllocation streamAllocation = engine.close();
          streamAllocation.release();
        }
      }

      Response response = engine.getResponse();
      Request followUp = engine.followUpRequest();

      if (followUp == null) {
        if (!forWebSocket) {
          engine.releaseStreamAllocation();
        }
        return response;
      }

      StreamAllocation streamAllocation = engine.close();

      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      if (!engine.sameConnection(followUp.url())) {
        streamAllocation.release();
        streamAllocation = null;
      } else if (streamAllocation.stream() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
      }

      request = followUp;
      engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,
          response);
    }
  }

首先 , 設(shè)置請(qǐng)求頭 , 拼接一條完整的請(qǐng)求, 然后初始化HttpEngine用于細(xì)節(jié)優(yōu)化 , 再開(kāi)始請(qǐng)求 , 請(qǐng)求完成后讀取請(qǐng)求結(jié)果.

  1. 開(kāi)始請(qǐng)求部分:
 public void sendRequest() throws RequestException, RouteException, IOException {
    if (cacheStrategy != null) return; // Already sent.
    if (httpStream != null) throw new IllegalStateException();
    // 首先 ,對(duì)請(qǐng)求頭再次做一次處理
    Request request = networkRequest(userRequest);
    // 獲取用戶(hù)設(shè)置的緩存
    InternalCache responseCache = Internal.instance.internalCache(client);
    // 從緩存中獲取之前請(qǐng)求過(guò)的Response
    Response cacheCandidate = responseCache != null
        ? responseCache.get(request)
        : null;

    long now = System.currentTimeMillis();
     // 根據(jù)請(qǐng)求和緩存結(jié)果來(lái)設(shè)置緩存策略,先從緩存拿,如果超時(shí),那么從網(wǎng)絡(luò)請(qǐng)求.
    cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).get();
    networkRequest = cacheStrategy.networkRequest;
    cacheResponse = cacheStrategy.cacheResponse;

    if (responseCache != null) {
      responseCache.trackResponse(cacheStrategy);
    }

    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }
...
...
try {
      // 建立鏈接, 使用socket創(chuàng)建,可以自動(dòng)選擇偏好鏈接, 選擇連接時(shí),首先從連接池來(lái)查找,
      // 沒(méi)有的話(huà)使用路徑選擇器,若還找不到的話(huà)就新建一個(gè)連接,同時(shí)將連接放到連接池中
      httpStream = connect();
      httpStream.setHttpEngine(this);

      if (writeRequestHeadersEagerly()) {
        long contentLength = OkHeaders.contentLength(request);
        if (bufferRequestBody) {
          if (contentLength > Integer.MAX_VALUE) {
            throw new IllegalStateException("Use setFixedLengthStreamingMode() or "
                + "setChunkedStreamingMode() for requests larger than 2 GiB.");
          }

          if (contentLength != -1) {
            // Buffer a request body of a known length.
            httpStream.writeRequestHeaders(networkRequest);
            requestBodyOut = new RetryableSink((int) contentLength);
          } else {
            // Buffer a request body of an unknown length. Don't write request headers until the
            // entire body is ready; otherwise we can't set the Content-Length header correctly.
            requestBodyOut = new RetryableSink();
          }
        } else {
          httpStream.writeRequestHeaders(networkRequest);
          requestBodyOut = httpStream.createRequestBody(networkRequest, contentLength);
        }
      }
      success = true;
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (!success && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末纸俭,一起剝皮案震驚了整個(gè)濱河市花沉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌森爽,老刑警劉巖锹锰,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朋截,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡逾苫,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)枚钓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)铅搓,“玉大人,你說(shuō)我怎么就攤上這事秘噪±晖蹋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵指煎,是天一觀的道長(zhǎng)蹋偏。 經(jīng)常有香客問(wèn)我,道長(zhǎng)至壤,這世上最難降的妖魔是什么威始? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮像街,結(jié)果婚禮上黎棠,老公的妹妹穿的比我還像新娘。我一直安慰自己镰绎,他們只是感情好脓斩,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著畴栖,像睡著了一般随静。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吗讶,一...
    開(kāi)封第一講書(shū)人閱讀 49,792評(píng)論 1 290
  • 那天燎猛,我揣著相機(jī)與錄音,去河邊找鬼照皆。 笑死重绷,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的膜毁。 我是一名探鬼主播昭卓,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼爽茴!你這毒婦竟也來(lái)了葬凳?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤室奏,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后劲装,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體胧沫,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昌简,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绒怨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纯赎。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖南蹂,靈堂內(nèi)的尸體忽然破棺而出犬金,到底是詐尸還是另有隱情,我是刑警寧澤六剥,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布晚顷,位于F島的核電站,受9級(jí)特大地震影響疗疟,放射性物質(zhì)發(fā)生泄漏该默。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一策彤、第九天 我趴在偏房一處隱蔽的房頂上張望栓袖。 院中可真熱鬧,春花似錦店诗、人聲如沸裹刮。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)捧弃。三九已至,卻和暖如春恕洲,著一層夾襖步出監(jiān)牢的瞬間塔橡,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工霜第, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留葛家,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓泌类,卻偏偏與公主長(zhǎng)得像癞谒,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子刃榨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

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

  • OKHttp牛逼之處 1.支持HTTP2/SPDY黑科技 --->okHttp中分包就分為Http1 和Http2...
    河里的枇杷樹(shù)閱讀 228評(píng)論 0 0
  • 參考資源 官網(wǎng) 國(guó)內(nèi)博客 GitHub官網(wǎng) 鑒于一些關(guān)于OKHttp3源碼的解析文檔過(guò)于碎片化弹砚,本文系統(tǒng)的,由淺入...
    風(fēng)骨依存閱讀 12,482評(píng)論 11 82
  • 最近抽時(shí)間分析一下 OKHttp3 的源碼枢希,關(guān)于 OKHttp 源碼解析的資料已經(jīng)有很多了桌吃,但是關(guān)于 OKHttp...
    lijiankun24閱讀 343評(píng)論 0 1
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)苞轿,斷路器茅诱,智...
    卡卡羅2017閱讀 134,629評(píng)論 18 139
  • 在上一篇文章中逗物,主要梳理了 OKHttp 請(qǐng)求的整體流程,了解到攔截器在其中有非常重要的意義瑟俭,那么本篇文章就重點(diǎn)介...
    lijiankun24閱讀 811評(píng)論 0 3