Okio 知多少

122 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

Okio

Okio是一个库,它补充了java.io和java.nio,使访问、存储和处理数据变得更加容易。

特点

  • 它是基于流的,⽽且是单向的,输⼊源叫 Source,输出⽬标叫 Sink
  • ⽀持 Buffffer
    • 向 NIO ⼀样,可以对 Buffffer 进⾏操作
    • 但不强制使⽤ Buffffer

使用

Okio的使用非常方便,相比java.io库它简化了很多繁杂的东西。这其中最核心的两个类:
Sink与Source代表的分别就是传统的输出流与输入流。

写数据到文件
private void writeFile() {
    File dir = new File(ROOT);
    File file = new File(dir,"Okio.txt");
    //文件是否存在,不存在就创建
    if(!file.exists()){
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    try{
        //获取sink对象
        Sink write = Okio.sink(file);
        //获取sink缓冲对象
        BufferedSink bufferedSink = Okio.buffer(write);
        //写入数据
        bufferedSink.writeUtf8("arrom!\r\n");
        bufferedSink.writeUtf8("Hello Word\r\n");
        //关闭sink
        bufferedSink.close();
        writeSink.close();
    }catch (Exception e){
        e.printStackTrace();
    }
}

读文件
private void readFile() {
    File file = new File(ROOT,"Okio.txt");
    //文件是否存在
    if(!file.exists()){
        return;
    }
    try{
        //获取source对象
        Source read = Okio.source(file);
        BufferedSource bufferedSource = Okio.buffer(read);
        String result = null;
        //循环读取数据
        while ((result = bufferedSource.readUtf8Line()) != null){
            System.out.println("=====result=====" + result);
        }
        //关闭source
        bufferedSource.close();
        read.close();
    }catch (Exception e){
        e.printStackTrace();
    }
}

关键类的介绍

ByteStrings and Buffers
  • ByteString 是不可变的字节序列。 对于字符数据,最基本的就是 String。而 ByteString 就像是 String 的兄弟一般,它使得将二进制数据作为一个变量值变得容易。这个类很聪明:它知道如何将自己编码和解码为十六进制、base64 和 utf-8。
  • Buffer 是一个可变的字节序列。 像 Arraylist 一样,你不需要预先设置缓冲区的大小。你可以将缓冲区读写为一个队列:将数据写到队尾,然后从队头读取。

在内部,ByteString和Buffer做了一些聪明的事情来节省CPU和内存。如果您将UTF-8字符串编码为ByteString,它会缓存对该字符串的引用,这样,如果您稍后对其进行解码,就不需要做任何工作。

Buffer 是作为片段的链表实现的。当您将数据从一个缓冲区移动到另一个缓冲区时,它会重新分配片段的持有关系,而不是跨片段复制数据。这对多线程特别有用:与网络交互的子线程可以与工作线程交换数据,而无需任何复制或多余的操作。

Sources and Sinks

Okio 有自己的 stream 类型: Source 和 Sink,分别类似于 java 的 Inputstream 和Outputstream,但是有一些关键区别:

  • 超时(Timeouts)。流提供了对底层 I/O 超时机制的访问。与java.io 的 socket 字流不同,read() 和 write() 方法都给予超时机制。
  • 易于实施。source 只声明了三个方法:read()、close() 和 timeout()。没有像available()或单字节读取这样会导致正确性和性能意外的危险操作。
  • 使用方便。虽然 source 和 sink 的实现只有三种方法可写,但是调用方可以实现 Bufferedsource 和 Bufferedsink 接口, 这两个接口提供了丰富API能够满足你所需的一切。
  • 字节流和字符流之间没有人为的区别。都是数据。你可以以字节、UTF-8 字符串、big-endian 的32位整数、little-endian 的短整数等任何你想要的形式进行读写;不再有InputStreamReader
  • 易于测试。Buffer 类同时实现了 BufferedSource 和 BufferedSink 接口,即是 source 也是 sink,因此测试代码简单明了。
Segment

Segment在 Okio 中作为数据缓冲的载体,一个 Segment 的数据缓冲大小为 8192,即 8k。每一个 Segment 都有前驱和后继结点,也就是说 Sement 是一个双向链表链表,准确的来说是一个双向循环链表。读取数据从 Segment 头结点读取,写数据从 Segment 尾结点写。

Okio 中引入池的概念也就是源码中SegmentPool的实现。SegmentPool 负责 Segment 创建和销毁,SegmentPool 最大可以缓存 8 个 Segment。

SegmentPool 是一个静态方法,因此也就是全局缓存只有 64 kb;

Okio的整体设计

image.png