09 | Android 高級進(jìn)階(源碼剖析篇) Square 高效易用的 IO 框架 okio(二)

作者簡介:ASCE1885博脑, 《Android 高級進(jìn)階》作者。
本文由于潛在的商業(yè)目的,未經(jīng)授權(quán)不開放全文轉(zhuǎn)載許可泥兰,謝謝!
本文分析的源碼版本已經(jīng) fork 到我的 Github题禀。

向日葵

前文中我們了解了 Java I/O 的歷史和 okio 的基本概念鞋诗,本文將開始 okio 中的輸入流 Source 的剖析,首先看下它的類結(jié)構(gòu)圖:

類結(jié)構(gòu)圖

可以看到迈嘹,層次還是挺豐富的削彬,平時用的最多的自然是 Source --> BufferedSource --> RealBufferedSourceBuffer。既然是 Source 專場秀仲,那么我們就先來聊聊 Source 的基本概念融痛。

注意這里的 Source 是一個統(tǒng)稱的概念,并不是指上圖中的 Source 這個接口神僵。

Source 是對輸入字節(jié)流的封裝雁刷,通過它提供的接口,我們可以從網(wǎng)絡(luò)挑豌,本地存儲或者內(nèi)存緩存中讀取數(shù)據(jù)安券,同時它也可以提供對數(shù)據(jù)的轉(zhuǎn)換操作墩崩,例如解壓,解密等侯勉。和 java.io 的 InputStream 相比鹦筹,兩者實現(xiàn)的功能其實是一樣的。但 InputStream 處理異構(gòu)數(shù)據(jù)時需要多種輸入流址貌,例如使用 DataInputStream 處理基本數(shù)據(jù)類型的數(shù)據(jù)铐拐, 使用 BufferedInputStream 增加緩存能力,使用 InputStreamReader 用來讀取字符串?dāng)?shù)據(jù)等练对,而 Source 體系中的 BufferedSource 能夠滿足所有這些需求遍蟋。接下來我們就來細(xì)細(xì)品味 Source 體系中的各個成員,首先從 Source 接口開始螟凭。

Source 接口

Source 是輸入流虚青,存在需要關(guān)閉的數(shù)據(jù),因此螺男,Source 接口繼承了 Closeable 并在 close 方法中關(guān)閉資源棒厘。同時定義了從 Buffer 中讀取指定個數(shù)的字節(jié)信息的 read 方法,相比 InputStream下隧,Source 接口定義了 timeout 方法實現(xiàn)超時機(jī)制奢人,代碼如下所示:

public interface Source extends Closeable {
  long read(Buffer sink, long byteCount) throws IOException;
  
  Timeout timeout();
  
  @Override void close() throws IOException;
}

BufferedSource 接口

BufferedSource 接口在 Source 接口的基礎(chǔ)上,主要做了兩件事:

  • 利用內(nèi)部定義的緩沖區(qū) Buffer 來提高數(shù)據(jù)讀取的性能
  • 定義一系列讀取基本數(shù)據(jù)類型和字符串類型的方法淆院,例如 readByte 讀取一個字節(jié)的信息何乎,readShort 讀取兩個字節(jié)的信息,readInt 讀取四個字節(jié)的信息土辩,readLong 讀取八個字節(jié)的信息支救,readByteString 讀取字節(jié)信息并轉(zhuǎn)換為 ByteString 類型,readByteArray 讀取字節(jié)數(shù)組信息脯燃,readUtf8 讀取字節(jié)信息并轉(zhuǎn)換為 UTF-8 編碼的字符串等搂妻。

BufferedSource 作為接口蒙保,具體的實現(xiàn)在它的實現(xiàn)類 RealBufferedSource 類和 Buffer 類中辕棚。這里需要說明一下,RealBufferedSource 內(nèi)部也是使用 Buffer 類提供的接口來實現(xiàn)底層的數(shù)據(jù)讀取等操作邓厕,我們可以將 RealBufferedSource 當(dāng)作對 Buffer 類的一個代理逝嚎,它在 Buffer 類提供的方法基礎(chǔ)上增加一些校驗等邏輯。這三者的關(guān)系如下圖所示:

三者關(guān)系圖

RealBufferedSource 類

既然 RealBufferedSource 類是代理類详恼,那么可以預(yù)知它本身的代碼邏輯不會很復(fù)雜补君,例如在實現(xiàn) Source 接口提供的 read 方法時,它首先對入?yún)⑦M(jìn)行校驗昧互,當(dāng)參數(shù)為 null 或者不符合邏輯時拋出相應(yīng)的異常挽铁,接著確保內(nèi)部緩沖區(qū) buffer 中有數(shù)據(jù)伟桅,然后根據(jù)緩存區(qū) buffer 中的字節(jié)數(shù)和 read 方法想讀取的字節(jié)數(shù)決定最終可以讀取的字節(jié)數(shù),最后調(diào)用 Buffer 類的實現(xiàn)的 Source 接口的同名方法叽掘,代碼如下所示:

/**
* 將數(shù)據(jù)從輸入流 source 中讀取到緩沖區(qū) buffer楣铁,然后經(jīng)過 buffer 將數(shù)據(jù)寫入輸出流 sink 中
*/
@Override public long read(Buffer sink, long byteCount) throws IOException {
    // 入?yún)⑿r?    if (sink == null) throw new IllegalArgumentException("sink == null");
    if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
    if (closed) throw new IllegalStateException("closed");

    // 確保緩存區(qū) buffer 中有數(shù)據(jù),一次最多從 source 中讀取一個 Segment 大小的數(shù)據(jù)塊
    if (buffer.size == 0) {
      long read = source.read(buffer, Segment.SIZE);
      if (read == -1) return -1;
    }

    // 決定最終可以讀取的字節(jié)大小
    long toRead = Math.min(byteCount, buffer.size);
    
    return buffer.read(sink, toRead);
  }

這個類中其他方法也都是類似的校驗邏輯更扁,我們就不細(xì)說了盖腕,但有一段代碼除外,那就是將 Source 轉(zhuǎn)換為 InputStream 的邏輯浓镜,它位于 inputStream 方法中溃列,這個方法也是實現(xiàn)自 BufferedSource 接口的。那么如何實現(xiàn) Source 和 InputStream 的轉(zhuǎn)換呢膛薛?由于 Source 是 okio 自身定義的概念听隐,因此,想要轉(zhuǎn)換成 InputStream 也就只能用最直接的方法哄啄,就是創(chuàng)建一個 InputStream遵绰,我們知道 InputStream 是一個抽象類,那么繼承這個抽象類并實現(xiàn)它的抽象方法就可以了增淹,當(dāng)然椿访,也可以用匿名內(nèi)部類的方式,RealBufferedSource 中的 inputStream 方法就是使用匿名內(nèi)部類的方式虑润,也就是直接 new 出一個 InputStream成玫,并實現(xiàn)它的抽象方法和重寫部分方法,代碼實現(xiàn)中拳喻,依然主要是作校驗操作哭当,具體數(shù)據(jù)讀操作交由 Buffer 類。

還有 54% 的精彩內(nèi)容
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
支付 ¥5.20 繼續(xù)閱讀
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市霎褐,隨后出現(xiàn)的幾起案子氧卧,更是在濱河造成了極大的恐慌,老刑警劉巖彻采,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捌归,居然都是意外死亡肛响,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門惜索,熙熙樓的掌柜王于貴愁眉苦臉地迎上來特笋,“玉大人,你說我怎么就攤上這事巾兆×晕铮” “怎么了虎囚?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蔫磨。 經(jīng)常有香客問我溜宽,道長,這世上最難降的妖魔是什么质帅? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任适揉,我火速辦了婚禮,結(jié)果婚禮上煤惩,老公的妹妹穿的比我還像新娘嫉嘀。我一直安慰自己,他們只是感情好魄揉,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布剪侮。 她就那樣靜靜地躺著,像睡著了一般洛退。 火紅的嫁衣襯著肌膚如雪瓣俯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天兵怯,我揣著相機(jī)與錄音彩匕,去河邊找鬼。 笑死媒区,一個胖子當(dāng)著我的面吹牛驼仪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播袜漩,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼绪爸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宙攻?” 一聲冷哼從身側(cè)響起奠货,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎座掘,沒想到半個月后递惋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡雹顺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年丹墨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嬉愧。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖喉前,靈堂內(nèi)的尸體忽然破棺而出没酣,到底是詐尸還是另有隱情王财,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布裕便,位于F島的核電站绒净,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏偿衰。R本人自食惡果不足惜挂疆,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望下翎。 院中可真熱鬧缤言,春花似錦、人聲如沸视事。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽俐东。三九已至跌穗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間虏辫,已是汗流浹背蚌吸。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留砌庄,地道東北人套利。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像鹤耍,于是被迫代替她去往敵國和親肉迫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

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