如果看官从事Android开发,相信你们对Okhttp这个框架并不陌生,当然非墨以后也会解析它。今天我们所说的Okio这个库,实际上是Java平台上的一个IO装饰库,在Okhttp这个框架中实际上也引用了这个库。本章先带大家简单的入门,熟悉一个Okio的一些基本概念和设计原则。
[Okio github 地址:https://github.com/square/okio]
Okio在设计上非常简单,主要分成几个大分支:
- Source :Okio重新定义了流的名称,用Source代替InputStream,当然,实际上底层还是用Jre的InputStream来做流操作,只不过在Okio中,对于输入流的所有操作都包含在Source接口下。
2.Sink : Okio重新定义了流的名称Sink用于替代OutputStream,当然如同Source一样,底层一样用的OutputStream处理流操作。
3.Buffer:Buffer是Okio中的重头类。之所以说重头,并不是因为它有多少的代码量,而是这个类承担了很多有用且实际的功能。
4.Segment:是Buffer管理中的内存单位,类似操作系统中的分页式存储。
5.其他子类:其他子类包含有Source和Sink的各种缓冲类和装饰类。
这里我们将引入Okio中的demo代码,大家可以到Okio的github地址里找到这段代码。这段代码主要是为了在程序中读取一个png文件:
BufferedSource pngSource = Okio.buffer(Okio.source(in));
//构建buffersource流
ByteString header = pngSource.readByteString(PNG_HEADER.size());
//获取一个ByteString
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();
我们可以看到:
1.在代码的第一行,Okio将先通过一个source静态方法来构造一个Source对象。按照我们上面的理解,Source实际上就是一个输入流,只不过这个输入流就像是InputStream一样,只提供一些最为基本的读取操作。因此,为了让我们的输入流能实现更加丰富多彩的数据流格式,Okio需要在外面包装一个BufferedSource接口。
2.代码的第二行,实际上是通过BufferedSource来获取一个长度
为"PNG_HEADER".size()的ByteString对象,这个操作主要是为了给这个文件做一个简单的类型判断。在我们的字节码文件也有一样的验证部分,这一部分叫做文件"魔数"或者叫做"文件署名域"。
3.接下去,就是解析PNG文件的while(true)语句,基于while(true)的语法特性,我们基本可以断定png文件的存储是一种块状结构,我们不妨到百科上查一下PNG文件的格式是什么样的,下图是非墨摘自百度百科的png文件数据块结构图:
按照png文件块的规范:
第一个u4长度的数据代表数据块中的数据长度
第二个u4长度的数据代表数据块类型
第三部分代表真实数据
第四部分u4长度的数据是CRC校验位
其中,第三部分的类型取值如下表:
根据上面的知识储备,我们就很好理解在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);//读取数据到chunk
int crc = pngSource.readInt();//读取crc校验位
decodeChunk(type, chunk);
if (type.equals("IEND")) break;
我们可以看到,通过BufferedSource的转换,我们可以获取各式各样的数据格式,比如Int,utf等等。它这个东西,非常类似于DataInputStream对象。就这样,我们完成了一个Png文件的读取。