作者簡介:ASCE1885, 《Android 高級進階》作者吗冤。
本文由于潛在的商業(yè)目的便贵,未經授權不開放全文轉載許可,謝謝昂秃!
本文分析的源碼版本已經 fork 到我的 Github禀梳。
okio 是 Square 開源的一個 Java IO 框架,是對 java.io
和 java.nio
的補充肠骆,提供了更靈活易用的接口來處理數(shù)據(jù)流的輸入和輸出算途,最開始它是作為 okhttp 的一個基礎組件存在的,后面隨著 okhttp 的不斷發(fā)展逐漸剝離獨立出來蚀腿。我們知道嘴瓤,okio 和 okhttp 并不只局限在 Android 平臺中使用,事實上莉钙,它們是 Java 平臺通用的廓脆,在 Java 后端開發(fā)中也經常會用到,例如在著名的微服務框架 Spring Cloud 中磁玉,就可以通過配置使用 okhttp 來代替默認的 HttpClient停忿,從而支持 HTTP/2。
IO 和 NIO
在正式介紹 okio 之前蚊伞,我們有必要先來回顧一下 java.io
和 java.nio
的基礎知識席赂。I/O(input/output) 是計算機與外部世界之間的接口吮铭,也是一個應用與外部系統(tǒng)的接口。在 Java 編程中颅停,I/O 被形象的表述為流的概念沐兵。所有的 I/O 操作可以被看作是字節(jié)在流中的移動,一次一個字節(jié)便监。流中的 I/O 操作既可以用來與外部系統(tǒng)聯(lián)系扎谎,也可以用于內部實現(xiàn)字節(jié)和對象或者對象和字節(jié)之間的轉換。
java.io
(后面以 IO 代之) 和 java.nio
(后面以 NIO 代之) 可以從以下三方面作一個對比:
IO 是基于數(shù)據(jù)流的烧董,而 NIO 是基于數(shù)據(jù)塊的
IO 和 NIO 最重要的區(qū)別就是數(shù)據(jù)的打包和傳輸方式毁靶。IO 是在流中處理數(shù)據(jù),NIO 是在塊中處理數(shù)據(jù)逊移。
基于流的 I/O 系統(tǒng)一次處理一個或者多個字節(jié)预吆,當輸入流生產一個字節(jié)信息,輸出流就消費一個字節(jié)的信息胳泉。我們可以很容易的為流數(shù)據(jù)創(chuàng)建過濾器拐叉,并通過把不同的過濾器串聯(lián)起來從而實現(xiàn)復雜的流處理機制。當然扇商,在流中字節(jié)信息是沒有緩存的凤瘦,因此你不能在流中來回的移動數(shù)據(jù)讀取的指針,除非你先把字節(jié)信息在某個地方緩存起來案铺。
基于塊的 I/O 系統(tǒng)是在塊中處理數(shù)據(jù)的蔬芥,每一次操作都會生產或者消費一塊數(shù)據(jù),基于塊比基于流的方式處理數(shù)據(jù)速度更快控汉。你可以在緩沖區(qū)(Buffer)中來回移動數(shù)據(jù)讀取或者寫入指針笔诵,因此靈活性更強。但是我們在往緩沖區(qū)中寫入更多數(shù)據(jù)之前需要確保緩沖區(qū)中的數(shù)據(jù)能夠及時處理姑子,從而不會造成數(shù)據(jù)的覆蓋乎婿。因此,基于塊的 I/O 系統(tǒng)相比基于流的 I/O 系統(tǒng)而言顯得不怎么優(yōu)雅和簡潔街佑。
IO 是同步的谢翎,NIO 是異步的
IO 中各種各樣的流都是阻塞或者同步的,這意味著當一個線程調用流的 read()
或者 write()
方法時舆乔,在數(shù)據(jù)處理完之前該線程將始終處于阻塞的狀態(tài)岳服。而 NIO 是支持異步的,也就是一個線程收到數(shù)據(jù)的讀或者寫請求后希俩,可以將數(shù)據(jù)處理發(fā)送給通道(Channel)吊宋,同時不必等待數(shù)據(jù)處理完成就可以返回來繼續(xù)處理其他請求。
API 的差異
為了讓大家有個直觀的印象,我們就來看看 IO 和 NIO 在讀取一個文件時的代碼璃搜,首先來看下 IO 是如何讀取文件的拖吼,這里因為是讀取一個 txt 文件,因此使用 Reader 類这吻,其中 FileReader 底層是使用 InputStream 來讀取文件的:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class WithoutNIOExample {
public static void main(String[] args) {
BufferedReader br = null;
String sCurrentLine = null;
try {
br = new BufferedReader(
new FileReader("test.txt"));
while ((sCurrentLine = br.readLine()) != null) {
System.out.println(sCurrentLine);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null)
br.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
NIO 讀取文件時需要結合緩沖區(qū)(Buffer)和通道(Channel)一起使用吊档,代碼如下所示:
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class ReadFileWithFixedSizeBuffer {
public static void main(String[] args) throws IOException {
RandomAccessFile aFile = new RandomAccessFile
("test.txt", "r");
FileChannel inChannel = aFile.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (inChannel.read(buffer) > 0) {
buffer.flip();
for (int i = 0; i < buffer.limit(); i++) {
System.out.print((char) buffer.get());
}
buffer.clear();
}
inChannel.close();
aFile.close();
}
}
okio
okio 自身定義了一系列的概念,本文我們先來對其中四個核心的概念進行簡單的介紹唾糯。