Okio源码分析

square在开源社区的贡献是卓越的,这里是square在Android领域贡献的开源项目。

1. okio概念

  • okio是一个由square公司开发的开源库,它弥补了Java.io和java.nio的不足,能够更方便快速的读取、存储和处理数据。

  • okio有自己的流类型Source和Sink,对应于java.io的InputStream和OutputStream。

  • okio内部引入了ByteString和Buffer,提升了效率和性能。

  • okio引入了超时机制。

  • okio规模不大,代码精巧,是源码学习的好素材(okio-1.6.0.jar):

这里写图片描述

2. Source和Sink

Source代表输入流,Sink代表输出流,Source和Sink的实现逻辑基本相似,以Source为例,学习一下它的实现原理,首先来看一下Source的源码:

package okio;

import java.io.Closeable;
import java.io.IOException;

public interface Source extends Closeable {
  /**
   * 将此source输入流中的数据移动到sink中(至少1字节,至多byteCount字节)
   * 返回移动的字节数,source读完为空时返回-1
   */
  long read(Buffer sink, long byteCount) throws IOException;

  /** 超时机制 */
  Timeout timeout();

  /**
   * 关闭此source输入流并释放此source输入流持有的所有资源
   * 关闭后的source输入流不能再进行读取
   * 及时关闭source输入流
   */
  @Override void close() throws IOException;
}

source相比于java.io的InputStream精简很多,它的具体功能通过装饰器模式在它的装饰类中实现,整体的认识一下Source和它的装饰器的实现关系:

这里写图片描述

GzipSource为支持gzip压缩的实现类,InflaterSource为GzipSource服务,用于压缩;ForwardingSource是一个具有委托功能的抽象类。

其中BufferedSource为实现Source支持缓冲区的子类接口,其中定义了缓冲区及多种类型的读方法,源码如下:

package okio;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;

public interface BufferedSource extends Source {
  
  Buffer buffer();

  boolean exhausted() throws IOException;

  void require(long byteCount) throws IOException;

  boolean request(long byteCount) throws IOException;

  byte readByte() throws IOException;

  short readShort() throws IOException;

  short readShortLe() throws IOException;

  int readInt() throws IOException;
  
  ......
  
}

RealBufferedSource为BufferedSource的实现类,通常情况下我们对输入流的操作都是在操作RealBufferedSource,RealBufferedSource类中有两个主要参数,一个是Source对象,一个是新建的Buffer对象,而各种读方法都是通过Buffer来具体实现的,比如readByteArray方法:

@Override public byte[] readByteArray() throws IOException {
    buffer.writeAll(source);
    return buffer.readByteArray();
  }

可见虽然这个类叫RealBufferedSource,但是实际上只是一个保存Buffer对象的一个代理实现,真正的实现都是在Buffer中实现的,而正是通过Buffer的应用,才实现了okio的高效性。

3. Buffer

Buffer是BufferedSink和BufferedSource的实现类,因此它既可以用来读数据,也可以用来写数据。在Buffer的注释中说明了okio的高效性:

  • copy数组时仅仅改变底层字节数组的所有权,而不是把数据从一块内存复制到另一块内存中

  • 如ArrayList一样根据需要动态分配内存大小

  • 避免了数组创建时的zero-fill,降低了GC的频率。

Buffer是通过Segment和SegmentPool来实现以上高效功能的,Segment译为片段,okio将数据也就是Buffer分割成片段,同时Segment有前置节点和后置节点,构成了一个双向循环链表,如图:
Buffer和Segment的关系

分片之间使用链表连接,片中使用数组存储,兼具读的连续性和写的可插入性,Segment中并不是随意的使用数组存储数据,其内部维护着一个固定长度的字节数组。Segment源码分析如下:

  static final int SIZE = 2048;
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值