OkHttp 與責(zé)任鏈模式

大學(xué)上 UML 課程的時(shí)候總覺(jué)得設(shè)計(jì)模式遙不可及、無(wú)法理解,印象最深的是 UML 課程期末考試的最后一題是責(zé)任鏈模式相關(guān)的內(nèi)容睹簇,我沒(méi)有做出來(lái)掌眠。畢業(yè)后蕾盯,也曾抱著《設(shè)計(jì)模式·可復(fù)用面向?qū)ο筌浖幕A(chǔ)》從頭開(kāi)始看,總感覺(jué)一頭霧水蓝丙,少點(diǎn)什么级遭。

如今看來(lái)望拖,當(dāng)時(shí)還是見(jiàn)得太少,經(jīng)驗(yàn)不足挫鸽。

design_pattern.jpg

書中關(guān)于的責(zé)任鏈模式的主要例子是多視窗對(duì)用戶事件的處理说敏,Android 系統(tǒng)中 View 樹對(duì)觸摸事件的分發(fā)處理流程也用到了責(zé)任鏈模式。

該模式的核心想法是:

給多個(gè)對(duì)象處理一個(gè)請(qǐng)求的機(jī)會(huì)丢郊,從而解耦發(fā)送者和接受者盔沫,該請(qǐng)求沿著對(duì)象鏈傳遞直到其中一個(gè)對(duì)象處理它。

對(duì)于 Http 客戶端請(qǐng)求枫匾,兩個(gè)主要工作是處理緩存和網(wǎng)絡(luò)請(qǐng)求架诞,這兩部分都有機(jī)會(huì)處理 Http 請(qǐng)求,而且存在一種場(chǎng)景是離線狀態(tài)且緩存可用婿牍,這時(shí)的請(qǐng)求便只由緩存部分處理侈贷。采用責(zé)任鏈模式可以很優(yōu)雅地將緩存和網(wǎng)絡(luò)職責(zé)分離,而且 Cache 部分總是先于 Socket 處理請(qǐng)求等脂。

源碼

官網(wǎng)給的例子如下:

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

我看文章的時(shí)候總是很討厭大段大段的代碼俏蛮,因?yàn)槲易约簳?huì)把源碼打開(kāi),邊看邊理解上遥。但為了文章的完整性搏屑,不得不放一點(diǎn)源碼。

我們只看同步執(zhí)行的過(guò)程:

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

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

在這里我們可以清楚地看到整條鏈?zhǔn)接?Interceptor 數(shù)組構(gòu)成粉楚,依次是開(kāi)發(fā)者自定義的普通 Interceptor辣恋、處理 Cookie的 Interceptor、處理 Cache 的 Interceptor模软、創(chuàng)建連接的 Interceptor伟骨、開(kāi)發(fā)者自定義的網(wǎng)絡(luò) Interceptor(可以用于添加下載進(jìn)度功能)、真正發(fā)起網(wǎng)絡(luò)請(qǐng)求的 Interceptor燃异。

處理請(qǐng)求的傳遞的實(shí)現(xiàn)部分在RealInterceptorChain類里:

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    return response;
  }

每個(gè) Interceptor 里的 intercept 函數(shù)會(huì)攔截請(qǐng)求携狭,做自己的工作,然后調(diào)用 Chain 里的 proceed 函數(shù)回俐,將 index 增 1逛腿,傳遞 Request 給下一個(gè)攔截器處理。

CallServerInterceptor 是個(gè)例外仅颇,由于是整條處理鏈的最后一個(gè)攔截器单默,這里直接處理網(wǎng)絡(luò)請(qǐng)求,返回 Response忘瓦,不再繼續(xù)傳遞搁廓。

值得一提的是 CacheInterceptor 類對(duì) Http 協(xié)議的緩存設(shè)計(jì)做了完整的實(shí)現(xiàn),很適合拿來(lái)學(xué)習(xí)Http協(xié)議的緩存處理,具體的細(xì)節(jié)便不在這里細(xì)說(shuō)了枚抵。

小結(jié)

OkHttp 源碼讀起來(lái)確實(shí)很優(yōu)雅线欲,很順暢明场,但是如果是我們面對(duì)Http客戶端的實(shí)現(xiàn)需求汽摹,是否能想到這么優(yōu)雅的設(shè)計(jì)方案。和理解原理比起來(lái)苦锨,僅僅會(huì)使用庫(kù)還遠(yuǎn)遠(yuǎn)不夠逼泣;和設(shè)計(jì)實(shí)現(xiàn)比起來(lái),僅僅理解原理也同樣遠(yuǎn)遠(yuǎn)不夠舟舒。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拉庶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子秃励,更是在濱河造成了極大的恐慌氏仗,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夺鲜,死亡現(xiàn)場(chǎng)離奇詭異皆尔,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)币励,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門慷蠕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人食呻,你說(shuō)我怎么就攤上這事流炕。” “怎么了仅胞?”我有些...
    開(kāi)封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵每辟,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我干旧,道長(zhǎng)渠欺,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任莱革,我火速辦了婚禮峻堰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盅视。我一直安慰自己捐名,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布闹击。 她就那樣靜靜地躺著镶蹋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贺归,一...
    開(kāi)封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天淆两,我揣著相機(jī)與錄音,去河邊找鬼拂酣。 笑死秋冰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的婶熬。 我是一名探鬼主播剑勾,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼赵颅!你這毒婦竟也來(lái)了虽另?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤饺谬,失蹤者是張志新(化名)和其女友劉穎捂刺,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體募寨,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡族展,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绪商。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苛谷。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情趟佃,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布锣尉,位于F島的核電站,受9級(jí)特大地震影響决采,放射性物質(zhì)發(fā)生泄漏自沧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一树瞭、第九天 我趴在偏房一處隱蔽的房頂上張望拇厢。 院中可真熱鬧,春花似錦晒喷、人聲如沸孝偎。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)衣盾。三九已至寺旺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間势决,已是汗流浹背阻塑。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留果复,地道東北人陈莽。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像据悔,于是被迫代替她去往敵國(guó)和親传透。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

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