日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

android okio使用方法,Android 开源框架 Okio 原理剖析

發(fā)布時間:2024/10/12 Android 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android okio使用方法,Android 开源框架 Okio 原理剖析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Retrofit,OkHttp,Okio 是 Square 團(tuán)隊開源的安卓平臺網(wǎng)絡(luò)層三板斧,它們逐層分工,非常優(yōu)雅地解決我們對網(wǎng)絡(luò)請求甚至更廣泛的 I/O 操作的需求。其中最底層的 Okio 堪稱小而美,功能也更基礎(chǔ),應(yīng)用更廣泛。這次我們就對它進(jìn)行一個詳細(xì)的分析。本文的分析基于 Okio 截至 2016.8.4 的最新源碼,非常建議大家下載 Okio 源碼之后,跟著本文,過一遍源碼。

1,概覽

和分析 Retrofit 和 OkHttp 時不同,這次我們不是直接上來就開始看代碼,我們先看一下它的官方介紹,對它有一個感性的認(rèn)識,這也正是我們在進(jìn)行技術(shù)選型時首先應(yīng)該做的事情。

Okio 補(bǔ)充了 java.io 和 java.nio 的內(nèi)容,使得數(shù)據(jù)訪問、存儲和處理更加便捷。

它的主要功能都被封裝在 ByteString 和 Buffer 這兩個類中,整個庫也是圍繞這兩個類展開。

本文接下來的內(nèi)容也將圍繞這兩個類來展開,先建立一個感性的認(rèn)識,再詳細(xì)分析它們的使用及原理,最后我們會看一下 Retrofit、OkHttp 是如何使用 Okio 的,以及 Gzip 壓縮這個功能是如何設(shè)計實(shí)現(xiàn)的。

1.1,ByteString

string 這個詞本意是“串”,只不過在編程語言的世界中,我們基本都用它來指代“字符串”,其實(shí)字符串應(yīng)該叫 CharString,因此 ByteString 的意義也就很好理解了,“字節(jié)串”。

ByteString 代表一個 immutable 字節(jié)序列。對于字符數(shù)據(jù)來說,String 是非?;A(chǔ)的,但在二進(jìn)制數(shù)據(jù)的處理中,則沒有與之對應(yīng)的存在,ByteString 應(yīng)運(yùn)而生。它為我們提供了對串操作所需要的各種 API,例如子串、判等、查找等,也能把二進(jìn)制數(shù)據(jù)編解碼為十六進(jìn)制(hex),base64 和 UTF-8 格式。

它向我們提供了和 String 非常類似的 API:

獲取字節(jié):指定位置,或者整個數(shù)組;

編解碼:hex,base64,UTF-8;

判等,查找,子串等操作;

1.2,Source 和 Sink

在看 Buffer 之前,我們先看一下 Source 和 Sink。

Okio 吸收了 java.io 一個非常優(yōu)雅的設(shè)計:流(stream),流可以一層一層套起來,不斷擴(kuò)充能力,最終完成像加密和壓縮這樣復(fù)雜的操作。再次感謝 Stay 一針見血地指出這正是“修飾模式”的實(shí)踐。

修飾模式,是面向?qū)ο缶幊填I(lǐng)域中,一種動態(tài)地往一個類中添加新的行為的設(shè)計模式。就功能而言,修飾模式相比生成子類更為靈活,這樣可以給某個對象而不是整個類添加一些功能。

Okio 有自己的流類型,那就是 Source 和 Sink,它們和 InputStream 與 OutputStream 類似,前者為輸入流,后者為輸出流。

但它們還有一些新特性:

超時機(jī)制,所有的流都有超時機(jī)制;

API 非常簡潔,易于實(shí)現(xiàn);

Source 和 Sink 的 API 非常簡潔,為了應(yīng)對更復(fù)雜的需求,Okio 還提供了 BufferedSource 和 BufferedSink 接口,便于使用(按照任意類型進(jìn)行讀寫,BufferedSource 還能進(jìn)行查找和判等);

不再區(qū)分字節(jié)流和字符流,它們都是數(shù)據(jù),可以按照任意類型去讀寫;

便于測試,Buffer 同時實(shí)現(xiàn)了 BufferedSource 和 BufferedSink 接口,便于測試;

Source 和 InputStream 互相操作,我們可以把它們等同對待,同理 Sink 和 OutputStream 也可以等同對待。

1.3,Buffer

我們看一下 Buffer 的類圖:

這里我們就可以看到,它集 BufferedSource 和 BufferedSink 的功能于一身,為我們提供了訪問數(shù)據(jù)緩沖區(qū)所需要的一切 API。

Buffer 是一個可變的字節(jié)序列,就像 ArrayList 一樣。我們使用時只管從它的頭部讀取數(shù)據(jù),往它的尾部寫入數(shù)據(jù)就行了,而無需考慮容量、大小、位置等其他因素。

和 ByteString 一樣,Buffer 的實(shí)現(xiàn)也使用了很多高性能的技巧。它內(nèi)部使用一個雙向 Segment 鏈表來保存數(shù)據(jù),Segment 是對一小段字節(jié)數(shù)組的封裝,保存了這個字節(jié)數(shù)組的一些訪問信息,數(shù)據(jù)的移動通過 Segment 的轉(zhuǎn)讓完成,避免了數(shù)據(jù)拷貝的開銷。而且 Okio 還實(shí)現(xiàn)了一個 Segment 對象池,以提高我們分配和釋放字節(jié)數(shù)組的效率。

2,ByteString 詳解

ByteString 整個類不到 500 行,完全可以通讀,但我們還是從實(shí)際的使用例子出發(fā)。

private static final ByteString PNG_HEADER = ByteString.decodeHex("89504e470d0a1a0a");

public void decodePng(InputStream in) throws IOException{

BufferedSource pngSource = Okio.buffer(Okio.source(in));

ByteString header = pngSource.readByteString(PNG_HEADER.size());

if (!header.equals(PNG_HEADER)) {

throw new IOException("Not a PNG.");

}

// ...

pngSource.close();

}

這里我們可以看到,我們可以直接從十六進(jìn)制字符串得到它所表示的字節(jié)串,我們看看它的內(nèi)部實(shí)現(xiàn):

public static ByteString decodeHex(String hex){

// ...

byte[] result = new byte[hex.length() / 2];

for (int i = 0; i < result.length; i++) {

int d1 = decodeHexDigit(hex.charAt(i * 2)) << 4;

int d2 = decodeHexDigit(hex.charAt(i * 2 + 1));

result[i] = (byte) (d1 + d2);

}

return of(result);

}

private static int decodeHexDigit(char c){

if (c >= '0' && c <= '9') return c - '0';

if (c >= 'a' && c <= 'f') return c - 'a' + 10;

if (c >= 'A' && c <= 'F') return c - 'A' + 10;

throw new IllegalArgumentException("Unexpected hex digit: " + c);

}

我們可以看到,它其實(shí)就是把每個字符所對應(yīng)的十六進(jìn)制值,保存到一個字節(jié)數(shù)組中,然后利用 of 這個工廠方法構(gòu)造一個 ByteString 對象。

那我們再看一下它的判等是怎么實(shí)現(xiàn)的:

@Override public boolean equals(Object o){

if (o == this) return true;

return o instanceof ByteString

&& ((ByteString) o).size() == data.length

&& ((ByteString) o).rangeEquals(0, data, 0, data.length);

}

public boolean rangeEquals(int offset, byte[] other, int otherOffset, int byteCount){

return offset >= 0 && offset <= data.length - byteCount

&& otherOffset >= 0 && otherOffset <= other.length - byteCount

&& arrayRangeEquals(data, offset, other, otherOffset, byteCount);

}

public static boolean arrayRangeEquals(

byte[] a, int aOffset, byte[] b, int bOffset, int byteCount){

for (int i = 0; i < byteCount; i++) {

if (a[i + aOffset] != b[i + bOffset]) return false;

}

return true;

}

不出所料,果然就是把指定范圍內(nèi)的字節(jié)逐個對比!當(dāng)然就是這樣,因?yàn)槲覀儗Υ嗟鹊亩x本來就是這樣的。

其他的方法這里就不一一展開,不過其中有兩點(diǎn)高性能實(shí)現(xiàn)技巧值得一提:

把一個 String 編碼為 utf8 時,會引用原 String,后面解碼時就可以直接返回了

由于 immutable,所以不怕被篡改,所以 toAsciiLowercase,toAsciiUppercase,substring 等函數(shù)的實(shí)現(xiàn)中,如果需要返回的內(nèi)容和自身一樣,那就會直接返回 this

3,Buffer 詳解

我們繼續(xù)看 PNG 解碼的例子:

public void decodePng(InputStream in) throws IOException{

BufferedSource pngSource = Okio.buffer(Okio.source(in));

ByteString header = pngSource.readByteString(PNG_HEADER.size());

if (!header.equals(PNG_HEADER)) {

throw new IOException("Not a PNG.");

}

while (true) {

Buffer chunk = new Buffer();

// Each chunk is a length, type, data, and CRC offset.

int length = pngSource.readInt();

String type = pngSource.readUtf8(4);

pngSource.readFully(chunk, length);

int crc = pngSource.readInt();

decodeChunk(type, chunk);

if (type.equals("IEND")) break;

}

pngSource.close();

}

我們先看看 Okio.buffer(Okio.source(in)) 做了些什么:

``` java

public static Source source(InputStream in) {

return source(in, new Timeout());

}

private static Source source(final InputStream in, final Timeout timeout) {

// ...

return new Source() {

@Override public long read(Buffer sink, long byteCount) throws IOException {

與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的android okio使用方法,Android 开源框架 Okio 原理剖析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。