Okio 之初探黄龙

Okio 是一个包装了 java.iojava.nio api 的库,以便可以更容易的访问、存储以及处理数据。

ByteStrings 和 Buffers

Okio 是围绕着两个容器类构建起来的:

  • ByteString 是一个不可变的字节序列。对于字符数据,Java 中的 String 类提供了基本的支持。ByteString 就像是 String 失散多年的兄弟,对二进制数据提供了基本的支持。这个类是非常人性化的:它明白如何编码或解码自身来作为十六进制字符串、base64 字符串或者 UTF-8 字符串。
  • Buffer 是一个可变的字节序列。就像 ArrayList 一样,你不需要提前规定你的 buffer 的大小。你可以像读写队列一样读写 buffer:写数据到末尾、从头部读数据。你不需要负责管理 positions, limits, 以及 capacities

ByteStringBuffer 内部做了一些优化以便节省 CPU 和内存资源。如果你把一个 UTF-8 的字符串转换为 ByteStringByteString 会缓存这个字符串的引用,之后如果你在解码时就直接返回这个字符串引用即可,无需做什么额外的工作。
Buffer 是由段(segment)链表组成的。当你从一个 Buffer 移动数据到另外一个 Buffer 时,它只是重新设置了段(segment)的拥有者而不是复制数据。这种方法对于多线程程序将是非常有用的:网络线程可以和工作者线程交换数据而无需任何的复制。

Sources 和 Sinks

java.io 原生 API 设计精妙的一处就是流数据可以分层进行转换处理,比如:加密和压缩。就像 InputStreamOutputStream,Okio 也包含了自己的流类型 SourceSink ,但是却有几点关键性的不同之处:

  • 超时机制。Okio 的流类型对底层 I/O 访问时增加了超时机制,而 java.io 套接字流的 read()write() 方法均是永不超时的。
  • 实现简单。Source 类声明了三个方法:read(), close(), 和 timeout(),不存在像 available() 这种危险的方法或者单字节读取数据这种容易造成性能问题的方法。
  • 容易使用。尽管 SourceSink 只有三个方法来读写数据,但是调用者可以使用功能丰富的实现类 BufferedSourceBufferedSink 接口。
  • 对于字节流和字符流没有人工区别。Okio 中字节流和字符流都是一样的,可以以字节流,UTF-8 字符串,大端 32位整数,小端短整数来读写。不再有 InputStreamReader
  • 容易测试。Buffer 类实现了 BufferedSourceBufferedSink 接口,所以你的测试代码将是简单清晰的。

Sources 加 sinks 和 InputStreamOutputStream 内部是可以互相操作的。所以你可以把 Source 看做一个 InputStream,并且也可以把 InputStream 看做一个 SourceSinkOutputStream 同样如此。

示例:一个 PNG 解码器

package okio;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Created by clearbug on 2017/11/5.
 */
public class PngDecoder {
    private static final ByteString PNG_HEADER = ByteString.decodeHex("89504e470d0a1a0a");

    public static void main(String[] args) throws IOException {
        InputStream inputStream = new FileInputStream("/Users/clearbug/Pictures/Small-mario.png");
        PngDecoder pngDecoder = new PngDecoder();
        pngDecoder.decodePng(inputStream);
    }

    public void decodePng(InputStream in) throws IOException {
        try (BufferedSource pngSource = Okio.buffer(Okio.source(in))) {
            ByteString header = pngSource.readByteString(PNG_HEADER.size());
            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;
            }
        }
    }

    private void decodeChunk(String type, Buffer chunk) {
        if (type.equals("IHDR")) {
            int width = chunk.readInt();
            int height = chunk.readInt();
            System.out.printf("%08x: %s %d x %d%n", chunk.size(), type, width, height);
        } else {
            System.out.printf("%08x: %s%n", chunk.size(), type);
        }
    }
}
posted @ 2017-11-05 11:23  optor  阅读(1008)  评论(0编辑  收藏  举报