作者簡介:ASCE1885博脑, 《Android 高級進(jìn)階》作者。
本文由于潛在的商業(yè)目的,未經(jīng)授權(quán)不開放全文轉(zhuǎn)載許可泥兰,謝謝!
本文分析的源碼版本已經(jīng) fork 到我的 Github题禀。
前文中我們了解了 Java I/O 的歷史和 okio 的基本概念鞋诗,本文將開始 okio 中的輸入流 Source 的剖析,首先看下它的類結(jié)構(gòu)圖:
可以看到迈嘹,層次還是挺豐富的削彬,平時用的最多的自然是 Source --> BufferedSource --> RealBufferedSource
和 Buffer
。既然是 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)系如下圖所示:
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 類。