一文詳解 Okio 輸入輸出流

2023-07-14 12:01:19

在 OkHttp 的原始碼中,我們經常能看到 Okio 的身影,這篇文章,我們把Okio拿出來進行一個詳細的介紹學習。

  • 輸入輸出的概念簡述
  • Okio 簡介
  • 工程中引入 Okio
  • API 簡介及使用介紹

一、輸入輸出

在正式介紹 Okio 之前,讓我們先回憶一下輸入/輸出流的概念。

  • 輸入流:外設——>記憶體
    將資料從各種外設(如鍵盤、檔案、網路、資料庫等)讀取到記憶體中;
  • 輸出流:記憶體——>外設
    與輸入流相反,是將記憶體資料寫入到各種外設(如檔案、網路、資料庫、顯示器等);

二、Okio簡介

Okio最初是作為OkHttp的一個元件出現,是 OkHttp 實現HTTP協定資料構建、解析中使用到的底層 IO 庫。其相比於傳統的 java.io 和 java.nio ,其在檔案網路等資料讀寫操作更加便捷、高效

Okio 的設計思想是將資料的讀寫操作封裝為一個統一的介面,即 SourceSink,其中 輸入為Source,輸出為Sink

  • Okio 還提供了 BufferByteString 用於封裝和操作位元組資料,提高資料讀寫的效率。
  • 同時,Okio 還提供了一些工具方法,例如從 InputStreamOutputStream 中建立 BufferedSourceBufferedSink 等。

三、引入Okio

Okio官方API地址為:
https://square.github.io/okio/

Okio Github開源地址為:
https://github.com/square/okio

使用 Okio 時,我們可以查閱官方最新版本,並通過如下方式引入Okio:

implementation("com.squareup.okio:okio:3.2.0")

四、API 簡介及使用

  • Buffer 簡介及API使用介紹
  • ByteString 簡介及API使用介紹
  • Source 和 Sink 使用介紹

4.1 Buffer

Buffer一個大小可變的位元組緩衝區,在Okio中BufferBufferedSourceBufferedSink的介面實現類,使用者實現位元組資料的緩衝與讀寫。

官方API描述如下:

  • Buffer可以像Arraylist一樣,不需要預先設定緩衝區的大小,而是隨著資料的增加自動擴充緩衝區大小
  • Buffer由很多的Segment片段構成,每個Segment中維護一個位元組陣列
  • Buffer中以連結串列的形式來管理Segment,當使用Buffer進行緩衝區位元組資料移動時,其只改變Segment位元組陣列的所有權,從而提高位元組陣列的移動效率。

okio.Buffer位元組緩衝區的使用方式舉例如下:

import okio.Buffer;

Buffer buffer = new Buffer();
// 向緩衝區寫入資料
buffer.writeUtf8("key");
buffer.writeByte('=');
buffer.writeUtf8("value");

// 緩衝區位元組大小
int byteCount = buffer.size();

// 讀取換區中的全部位元組資料
byte[] byteArray = buffer.readByteArray();
// 以Utf8編碼的形式輸出所有字串
String result = buffer.readUtf8();

// 清空緩衝區
buffer.clear();

4.2 ByteString

ByteString 中維護了大小不可變的位元組陣列,其可以對存入該位元組陣列的資料進行base64utf8md5sha256等字串的編解碼操作。
ByteString 更像是一個工具類,在Okio中其重要應用場景也是在網路傳輸中對資料進行編碼和解碼工作

官方API描述如下:

其部分靜態方法和公有方法如下圖所示:

okio.ByteString的使用方式舉例如下:

import okio.ByteString;

// utf8編碼
ByteString byteString = ByteString.encodeUtf8("hello");
// HEX
ByteString byteString = ByteString.decodeHex("hello");
// 輸出utf8字串
String result = byteString.utf8();

4.3 Source 和 Sink

Source 和 Sink 在前文中提到過輸入為Source,輸出為Sink。在 Okio 中,SourceSink 用於讀取寫入資料的抽象類,其提供了一組標準的IO讀寫方法,可以方便地進行資料的讀寫操作。

// Okio原始碼:輸入流 Source
// Source 介面類,最主要的方法是 read
public interface Source extends Closeable {
	// 讀位元組資料
    long read(Buffer var1, long var2) throws IOException;
    // timeout
    Timeout timeout();
    void close() throws IOException;
}
// Okio原始碼:輸出流 Sink 
// Sink 介面類,最主要的方法是 write
public interface Sink extends Closeable, Flushable {
	// 寫位元組資料
    void write(Buffer var1, long var2) throws IOException;
    void flush() throws IOException;
    Timeout timeout();
    void close() throws IOException;
}

在把前文已經展示過的Okio結構圖拿出來:

  • Source的最終實現類是RealBufferedSource
  • Sink的最終實現類是RealBufferedSink

使用 okio.Source 從檔案中讀取資料,程式碼舉例如下:

// 使用 Source 從檔案中讀取資料
public static void readLines(File file) throws IOException {
    // 輸入流
    Source fileSource = Okio.source(file);
    // 構建 BufferedSource
    RealBufferedSource bufferedSource = Okio.buffer(fileSource);
    // 迴圈讀取
    while (true) {
        // 讀取行資料
        String line = bufferedSource.readUtf8Line();
        if (line == null) {
            break;
        }
    }
}

使用 okio.Sink 向檔案中寫入資料,程式碼舉例如下:

// 使用 Sink 向檔案中寫入資料
public static void writeToFile(File file) throws IOException {
	// 建立輸出流
    Sink fileSink = Okio.sink(file);
    // 構造 BufferedSink
    RealBufferedSink bufferedSink = Okio.buffer(fileSink);
    // 向檔案中寫入資料
    bufferedSink.writeUtf8("Hello");
    bufferedSink.writeUtf8("\n");
    bufferedSink.writeAll(Okio.source(new File("my.txt")));
}

五、參考

Okio API:
https://square.github.io/okio/

Okio Github:
https://github.com/square/okio

Java流:
http://c.biancheng.net/view/1119.html

= THE END =

文章首發於公眾號」CODING技術小館「,如果文章對您有幫助,歡迎關注我的公眾號。