扫盲系列 - Okio 基本用法
Okio 同样是 Square 公司推出的增强型 IO 处理库, Square 出品,必属精品。 OKHttp 中间也会用到Okio
Okio 很方便的使用链式调用。
Sink 和Source
Okio 中最基本的接口。
- Sink 输出流 引入 write() 直接从传入的 source 缓存中读取数据并写入到自己的 Buffer 中
- Source 输入流接口。 引入了 read 方法用于直接将数据读取到传入的 Sink 的 Buffer 缓存中
BufferedSink 和 BufferedSource
提供一系列读写字节,字符数据的接口。
- BufferedSource 带有缓存功能的输入流接口,继承 Source ,提供一系列读字节,字符数据的接口。
- BufferedSink 带有缓存功能的输出流接口 继承 Sink 提供一系列读写字节,字符数据的接口。
Okio
入口类,也是工厂类
- 提供 source 方法得到一个 Source 输入流,
- 提供 sink 方法得到一个 Sink 输出流,
- 提供 buffer 方法得到具有缓存功能的 Source 或者 Sink 对象。
- 提供对 File , Socket ,和(InputStream,OutputStream)三种类型的源进行操作
Okio 是构建在(InputStream,OutputStream)之上的,得到封装之后的(Source,Sink)。
Segment
一小段数组数据的链式节点封装结构,由它们可以组成一个循环连接的双向链表队列。这样数据的复制和转移,以及 Segment 的内部相关优化,都十分方便高效。
- 有 prev 和 next 指针,可以找到前一个和后一个的 Segment 节点。
- 内部维护着一段固定大小 2048 的数组数据。 pos 记录下一个可读的位置, limit 记录下一个可写的位置,因此, 0 到 pos 的数据部分是已经标记读取过了的无效数据区域, pos 到 limit 之间的就是该 Segment 的有效数据区域, limit 到 Segment 大小部分是还可以再写入数据的区域。
- pop用于弹出当前 Segment 并返回下一个 Segment 。
- push用于压入一个 Segment 到当前 Segment 的后面。
- split用于一个 Segment 分割成两个相连的 Segment ,之前 Segment 的有效数据区域[offset,limit]被分割成前一个 Segment 的有效区域[offset,offset+byteCount]和后一个 Segment 的有效区域[offset+byteCount,limit]部分。
- 用于考虑将当前 Segment 和前一个 Segment 的进行合并。如果前一个 Segment 的可写区域[limt,Segment.SIZE]大于当前 Segment 的有效数据区域[pos, limit],则可以将当前 Segment 的有效数据写到前一个 Segment 中去,然后删除当前 Segment ,节省一个 Segment 空间。
SegmentPool
管理 Segment 的池,使用单链表记录无用的 Segment ,提供了 take 获取一个可用的 Segment ,提供 recycle 将无用的 Segment 进行回收或维护。如果 SegmentPool 中的 Segment 的数量小于 32 个, recycle 时会将它加入到单链表中记录起来,同时重置 pos 和 limit 以方便后期的 Segment 重用。如果超过了 32 个了,则 recycle 不进行任何操作,这将导致该 Segment 没有任何引用了,也就将会被回收了。 保存暂时不用的数据容器,防止频繁GC 基本上所有的 XX 池的作用都是防止已申请的资源被回收,增加资源的重复利用,提高效率,减少 GC ,避免内存抖动
Buffer
public final class Buffer implements BufferedSource , BufferedSink , Cloneable {}
Okio 的核心类,用于数据缓存的管理,它实现了 BufferedSource 和 BufferedSink 接口, 它还支持两个 Buffer 之间数据的转移,(转移的话就是数据指向发送改变了,速度不是拷贝能比的),这就是为啥 Buffer 这么牛逼的原因了,因为它是唯一一个既能进行读取数据管理,又能进行写入数据管理,而且相互之间还能直接数据转移操作,真是神一样的存在。
ByteStrings
不可变的字节序列,对于字符数据,最基本的是 String ,而 ByteString 就像是 String 的兄弟一般,它使得二进制数据作为一个变量变得容易,这个类很聪明, 它知道如何把自己的数据编码和解码为十六进制, base64 和utf-8
在内部, ByteString 和 Buffer 做了一些聪明的事情来节省 CPU 和内存。如果您将 UTF-8 字符串编码为 ByteString ,它会缓存对该字符串的引用,这样,如果您稍后对其进行解码,就不需要做任何工作。
RealBufferedSource,RealBufferedSink
- RealBufferedSource 缓存 Source 接口的具体实现,继承自 BufferedSource ,内部的操作基本都是有 Buffer 来参与处理的,首先会调用 request 来读取 source 里的一段数据到 Buffer 中,然后后续的读取数据都是从 Buffer 中读的。
- RealBufferedSink 缓存 Sink 接口的具体实现,继承自 BufferedSink 。内部的操作基本都是有 Buffer 来参与处理的,首先会将数据写到 Buffer 中,然后调用 emitCompleteSegments ,如果 Buffer 存储缓存数据的 size 小于 Segment 大小的一半,即 1024 的话,不会可以继续缓存,否则会将缓存的内容全部写到输出中。
整体架构图如下
例子
写文件
private static void wirteFile(File file) {
try {
BufferedSink buffer = Okio.buffer(Okio.sink(file));
buffer.writeUtf8("nihaoa ");
buffer.writeUtf8("\n这是第二行");
buffer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
BufferedSink 有一系列的 write 方法的,几乎可以处理各种数据类型的。
读文件
private static void readFile(File file) {
try {
BufferedSource source = Okio.buffer(Okio.source(file));
String readUtf8 = source.readUtf8();
String readUtf8Line = source.readUtf8Line();
System.out.println("readUtf8: " + readUtf8);
System.out.println("-----------");
System.out.println("readUtf8Line: " + readUtf8Line);
} catch (Exception e) {
e.printStackTrace();
}
}
结果是
readUtf8: nihaoa
这是第二行
-----------
readUtf8Line: null
可是把 readUtf8() readUtf8Line() 执行顺序换一下,
private static void readFile(File file) {
try {
BufferedSource source = Okio.buffer(Okio.source(file));
String readUtf8Line = source.readUtf8Line();
String readUtf8 = source.readUtf8();
System.out.println("readUtf8: " + readUtf8);
System.out.println("-----------");
System.out.println("readUtf8Line: " + readUtf8Line);
} catch (Exception e) {
e.printStackTrace();
}
}
结果就变成这样了
readUtf8: 这是第二行
-----------
readUtf8Line: nihaoa
readUtf8() 是把文件里面的数据都读取出来,而 readUtf8Line() 只是读取一行数据。
BufferedSource 同理也有一系列的 read 方法的,用来读取各种类型的数据的。
Gizip 压缩
private static void gzip(File file) {
try {
//zip压缩
GzipSink gzipSink = new GzipSink(Okio.sink(file));
BufferedSink bufferedSink = Okio.buffer(gzipSink);
bufferedSink.writeUtf8("this is zip file");
bufferedSink.flush();
bufferedSink.close();
//读取zip
GzipSource gzipSource = new GzipSource(Okio.source(file));
BufferedSource bufferedSource = Okio.buffer(gzipSource);
String s = bufferedSource.readUtf8();
bufferedSource.close();
System.out.println("GzipSource " + s);
} catch (Exception e) {
e.printStackTrace();
}
}
搬运地址:
既已览卷至此,何不品评一二: