Okio内部原理

27 阅读3分钟

Okio是Square公司的一个IO库,Square公司开发很多开源库都基于这套okio. 例如Retrofit, OkHttp, Moshi, Dagger。

它主要的两个优势

1, 简化InputStream和 OutputStream 的使用

2 ,效率提升

关于Source, Sink, Buffer

在Okio中,Source 用于读取, Sink用于写,Buffer作为一个缓存区

如果你使用过java的io stream, 那你大概用过Buffered{Input,Output}Stream进行二次封装,进行数据块读取,以提高读取效率。 Okio中基于Buffer 实现的 BufferedSInk和BufferedSoucrce也是基于此情况设计的。 但是区别它不是使用固定长度的数组进行读取,而是Segment。

关于 Segment

一个双向链表的数据结构,它有一个next和prev引用到其它的Segment。 它还有一个ByteArray 的 buffer. 这里面最有意思的是两个pointers 叫做 pos 和 limit 还有两个标志 shared 和 owner。

首先需要搞清楚Segment如何使用

image.png

内部结构来讲Buffer维护了一个圆形的双向Segment List. 在这个Segment List中有一个Head引用,可以通过它来访问buffer内容。

image.png

这样意味着buffer内容可以安全的增加,不会影响到Sink和Source的管道(pipe)的使用

image.png

相比传统的java io BufferedInputStream 和 BufferedOutputStream 的行为,在加入一些数据到buffer中可能导致级联刷新.

image.png

如果所示,这种级联刷新过程实际上会进行数据拷贝,但Okio中优化了这一点,我们知道了在okio中的buffer实际上是维护的一个segment list, 在出现pipe操作的时候,它不需要进行数据拷贝,仅仅移动segment的引用即可。

image.png

但是并不是每次都要把segment做整体移动,有时候仅仅需要移动segment中的部分数据,这个时候之前提到的pos和limit指针就派上了用场。它可以对当前的segment解除限制(limit),然后孵化出一个新的segment,这是一个非常轻量级的操作.

image.png

上图中孵化出来的segment和自己原本segment都有一个shared标志,这代表okio知道他们这块数据是分享的数据,不能进行其它的某些操作。举个例子在,不能往只有shared标志里面写数据,因为它只属于分享数据,但是owner是可以写入的。每一个ByteArray只能有一个owner.

这里还有一种操作情况是数据转移,仅存在于owner标志的segment中。想象一下你在使用下半部分数据,并且你想写入更多的数据到segment中,这个时候你可以把下半部分数据往前移动。但是分享后的segment是不支持该操作的,因为前半部分是被别的segment正在使用。

image.png

保持这套不变量需要很多针对segment和buffer的检查工作, 不过okio提供了足够的效率机制弥补了实现的逻辑的复杂性。

原文连接: medium.com/@jerzy.chal…