Okio 源码解析 : 一套精简高效的 I/O 库

本文深入解析了Okio库,一个用于替代常规IOStream的高性能库。Okio的核心在于其Segment机制,通过分段存储数据,支持数据共享和复用,提高内存利用率和I/O效率。Buffer接口是Okio的关键,它维护了一个Segment链表,实现了数据的高效读写。此外,Okio的Timeout机制确保了超时操作的中断,增强了系统稳定性。通过对BufferedSource和BufferedSink的封装,Okio提供了更便捷的读写操作。整体来看,Okio通过一系列优化,提升了Android应用的I/O性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

个人博客:https://blog.N0tExpectErr0r.cn

小专栏:https://xiaozhuanlan.com/N0tExpectErr0r

从前面的 OkHttp 源码解析中我们可以知道,OkHttp 中的 I/O 都不是通过我们平时所使用的 IOStream 来实现,而是使用了 Okio 这个第三方库,那它与寻常的 IOStream 有什么区别呢?让我们来分析一下它的源码。

Okio 中有两个非常重要的接口——Sink 以及 Source,它们都继承了 Closeable,其中 Sink 对应了我们原来所使用的 OutputStream,而 Source 则对应了我们原来所使用的 InputStream

Okio 的入口就是Okio 类,它是一个工厂类,可以通过它内部的一些 static 方法来创建 SinkSource 等对象。

Sink

Sink 实际上只是一个接口,让我们看看 Sink 中有哪些方法:

public interface Sink extends Closeable, Flushable {
   
   
  void write(Buffer source, long byteCount) throws IOException;

  @Override void flush() throws IOException;

  Timeout timeout();

  @Override void close() throws IOException;
}

可以看到,它主要包含了 writeflushtimeoutclose 这几个方法,我们可以通过 Okio.sink 方法基于 OutputStream 获取一个 Sink

private static Sink sink(final OutputStream out, final Timeout timeout) {
   
   
  if (out == null) throw new IllegalArgumentException("out == null");
  if (timeout == null) throw new IllegalArgumentException("timeout == null");
  return new Sink() {
   
   
    @Override public void write(Buffer source, long byteCount) throws IOException {
   
   
      checkOffsetAndCount(source.size, 0, byteCount);
      while (byteCount > 0) {
   
   
        timeout.throwIfReached();
        Segment head = source.head;
        int toCopy = (int) Math.min(byteCount, head.limit - head.pos);
        out.write(head.data, head.pos, toCopy);
        head.pos += toCopy;
        byteCount -= toCopy;
        source.size -= toCopy;
        if (head.pos == head.limit) {
   
   
          source.head = head.pop();
          SegmentPool.recycle(head);
        }
      }
    }
    @Override public void flush() throws IOException {
   
   
      out.flush();
    }
    @Override public void close() throws IOException {
   
   
      out.close();
    }
    @Override public Timeout timeout() {
   
   
      return timeout;
    }
    @Override public String toString() {
   
   
      return "sink(" + out + ")";
    }
  };
}

这里构建并实现了一个 Sink 的匿名内部类并返回,主要实现了它的 write 方法,剩余方法都是简单地转调到 OutputStream 的对应方法。

write 方法中,首先进行了一些状态检验,这里貌似在 Timeout 类中实现了对超时的处理,我们稍后再分析。之后从 Buffer 中获取了一个 Segment,并从中取出数据,计算出写入的量后将其写入 Sink 所对应的 OutputStream

Segment 采用了一种类似链表的形式进行连接,看来 Buffer 中维护了一个 Segment 链表,代表了数据的其中一段。这里将 Buffer 中的数据分段取出并写入了 OutputStream 中。

最后,通过 SegmentPool.recycle 方法对当前 Segment 进行回收。

从上面的代码中我们可以获取到如下信息:

  1. Buffer 其实就是内存中的一段数据的抽象,其中通过 Segment 以链表的形式保存用于存储数据。
  2. Segment 存储数据采用了分段的存储方式,因此获取数据时需要分段从 Segment 中获取数据。
  3. 有一个 SegmentPool 池用于实现 Segment 的复用。
  4. Segment 的使用有点类似链表。

Source

SourceSink 一样,也仅仅是一个接口:

public interface Source extends Closeable {
   
   
  long read(Buffer sink, long byteCount) throws IOException;

  Timeout timeout();

  @Override void close() throws IOException;
}

Okio 中可以通过 source 方法根据 InputStream 创建一个 Source

private static Source source(final InputStream in, final Timeout timeout) {
   
   
    if (in == null) {
   
   
        throw new IllegalArgumentException("in == null");
    } else if (timeout == null) {
   
   
        throw new IllegalArgumentException("timeout == null");
    } else {
   
   
        return new Source() {
   
   
            public long read(Buffer sink, long byteCount) throws IOException {
   
   
                if (byteCount < 0L) {
   
   
                    throw new IllegalArgumentException("byteCount < 0: " + byteCount);
                } else if (byteCount == 0L) {
   
   
                    return 0L;
                } else {
   
   
                    try {
   
   
                        timeout.throwIfReached();
                        Segment tail = sink.writableSegment(1);
                        int maxToCopy = (int)Math.min(byteCount, (long)(8192 - tail.limit));
                        int bytesRead = in.read(tail.data, tail.limit, maxToCopy);
                        if (bytesRead == -1) {
   
   
                            return -1L;
                        } else {
   
   
                            tail.limit += bytesRead;
                            sink.size += (long)bytesRead;
                            return (long)bytesRead;
                        }
                    } catch (AssertionError var7) {
   
   
                        if (Okio.isAndroidGetsocknameError(var7)) {
   
   
                            throw new IOException(var7);
                        } else {
   
   
                            throw var7;
                        }
                    }
                }
            }
            public void close() throws IOException {
   
   
                in.close();
            }
            public Timeout timeout() {
   
   
                return timeout;
            }
            public String toString()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值