Okio总体上来说是对io操作的一种良好封装。操作方便,性能也比系统自带的要好。

操作上的方便性:

  用了外观模式,将Stream File Socket的读操作封装成了Source接口,将Stream File Socket的写操作封装成了Sink接口

Okio.source 和 Okio.sink方法可以将上述三种类型封装成统一的接口。

为了使更方便点,不用关心数据拷贝,和存储数据空间管理问题又封装了BufferedSource,BufferedSink。

这两个接口里面有使用率频率很高的读写接口,包括字符读取,long,int等数据类型读写,不再面向byte操作

public void readLines(File file) throws IOException {
    try (BufferedSource source = Okio.buffer(Okio.source(file))) {
        for (String line; (line = source.readUtf8Line()) != null; ) {
        if (line.contains("square")) {
            System.out.println(line);
        }
        }
    }
}

public void writeEnv(File file) throws IOException {
  try (Sink fileSink = Okio.sink(file);
       BufferedSink bufferedSink = Okio.buffer(fileSink)) {

    for (Map.Entry<String, String> entry : System.getenv().entrySet()) {
      bufferedSink.writeUtf8(entry.getKey());
      bufferedSink.writeUtf8("=");
      bufferedSink.writeUtf8(entry.getValue());
      bufferedSink.writeUtf8("\n");
    }

  }
}

是不是清爽很多

在BufferedSource,BufferedSink接口之下隐藏的实现细节在RealBufferedSink,RealBufferedSource这两个类里面,这两个类里面采用Buffer实现,而Buffer内部与Segment相配合,Buffer和Segment是Okio的核心了。 Segment相当于原来我们相当朴素的写法中new出来的那个byte数组,Buffer内部需要要一个Segment时,就会从SegmentPool拿一个。

Segment内部封装了一个大小为8192大小的字节数组。

性能提升:

  性能的提升在于减少字节拷贝,java内部实现的BufferedInputStream BufferedOutputStream内部维护一个8192大小的字节数组。

写入数据时我们需要两个操作,首先将数据变换为byte数组,并写入我们自己准备buf内 然后将buf通过write接口写入内部的buf。

这里其实是有两次拷贝的。

而okio中我们只需拷贝一次。

Segment里还有shared字段,可以与其他Segment共享buf。

超时功能:

  普通的TimeOut功能是在每次读写时看一下是不是到了deadline时间,或线程被interrupt socket的TimeOut是在WatchDog线程内轮询,查找并回调timeout接口,在回调中close socket

okio作者jakewharton的一篇文章 Forcing bytes downward in Okio