Okio组件源码分析

Okio库 组件分析

Segment

  Segment 是数据操作过程中,保存数据的地方,内部持有一个 限制最大为 8192 byte 的byte[]数组用来存储数据。Segment 的结构是 双向链表的结构,所以提供了 pop push 对结构的操作。Segment 采用链表的结构,内部使用数组来保存存储数据是一个数据操作的这种方案,链表使得插入删除更快,数组可以保证读取更快。Segment 中的优化还有 Segment 数据快的共享 以及 数据的压缩机制。

  /**
   * Call this when the tail and its predecessor may both be less than half
   * full. This will copy data so that segments can be recycled.
   */
  public void compact() { //压缩当前Segment
    if (prev == this) throw new IllegalStateException();
    if (!prev.owner) return; // Cannot compact: prev isn't writable.
    int byteCount = limit - pos;
    int availableByteCount = SIZE - prev.limit + (prev.shared ? 0 : prev.pos);
    if (byteCount > availableByteCount) return; // Cannot compact: not enough writable space.
    writeTo(prev, byteCount); //判断可以则写入
    pop(); // pop当前segment
    SegmentPool.recycle(this); // pool 回收
  }

SegmentPool

  Segment的创建以及回收。因为Segment本身是链式结构,所以SegmentPool,内部使用 一个 next 作为 当前回收池第一个Segment的引用。SegmentPool 内部对 池的大小做了限制, Pool的最大字节是 64kb。不过看 这个 todo的注释,貌似也不确定 MAX_SIZE 设置为多少合适。

/** The maximum number of bytes to pool. */
  // TODO: Is 64 KiB a good maximum size? Do we ever have that many idle segments?
  static final long MAX_SIZE = 64 * 1024; // 64 KiB.

Source 与 Sink

  类似 java.io 中流的概念,流的设计可以层层嵌套,功能加强,Source 表示输入流,Sink 表示输出流。BufferedSourceBufferdSink 提供了 数据的缓冲处理。Buffer的封装后,内部使用 Segment 链表来保存数据,在具体的实现上,okio 还提供了 数据的共享。SourceSink 抽象了数据的来源,可以是 磁盘内部网络等。

对Buffer 设计的理解

  这里,实际上对Buffer 这一层的设计,我只理解了是要做数据的暂存,避免持续的读写直接从管道对应的底层硬件操作。Okio中,Buffer类 实现了 BufferedSource, BufferedSink,也就是Buffer类将Source 和 Sink的操作都集合在一起,而在实际的处理过程中,是由 RealBufferedSinkRealBufferedSource 处理 写入 和输出 的一些不同之处,共同的业务逻辑调用Buffer类来实现,(Real类 是 Buffere 的代理类)分别这里贴上一段别人对okio中Buffer这一层设计的理解。

public final class Buffer implements BufferedSource, BufferedSink, Cloneable {
  private static final byte[] DIGITS =
      { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
  static final int REPLACEMENT_CHARACTER = '\ufffd';

  Segment head;
  long size;

  public Buffer() {
  }
}  
final class RealBufferedSource implements BufferedSource {
  public final Buffer buffer = new Buffer();
  public final Source source;
  boolean closed;

  RealBufferedSource(Source source) {
    if (source == null) throw new NullPointerException("source == null");
    this.source = source;
  }
  //... 一些数据操作
}

Okio对数据的操作需要先从 管道(类似InputStream) 读到 Buffer 里(require),这个过程实际由匿名 Source 完成(提供了超时控制),再把数据从 Buffer 中读出来返回。

其实我们如果粗略看一下 RealBufferedSource 和 RealBufferedSink 这两个类,我们就会发现,它们读写逻辑的实现都比较绕:读操作都是先把数据从 Source 读到 Buffer,再把数据从 Buffer 读到输出(返回值或传入的输出参数);写操作都是先把数据从输入写到 Buffer,再把数据从 Buffer 写到 Sink。

为什么要这么倒腾? 让我们从功能需求和设计方案来考虑。

BufferedSource 要提供各种形式的读取操作,还有查找与判等操作。大家可能会想,那我就在实现类中自己实现不就好了吗?干嘛要经过 Buffer 中转呢?这里我们实现的时候,需要考虑效率的问题,而且不仅 BufferedSource 需要高效实现,BufferedSink 也需要高效实现,这两者的高效实现技巧,很大部分都是共通的,所以为了避免同样的逻辑重复两遍,Okio 就直接把读写操作都实现在了 Buffer 这一个类中,这样逻辑更加紧凑,更加内聚。而且还能直接满足我们对于“两用数据缓冲区”的需求:既可以从头部读取数据,也能向尾部写入数据。至于我们单独的读写操作需求,Okio 就为 Buffer 分别提供了委托类:RealBufferedSource 和 RealBufferedSink,实现好 Buffer 之后,它们两者的实现将非常简洁(前者 450 行,后者 250 行)。  

ByteString

  在大多数语言,包括Java中,String 是一个不可变的对象,ByteString 也是一个 不可变对象,ByteString 对象代表一个 immutable 的字节序列,主要的字段是 data 以及 utf8data即ByteString 构造时传入的 byte数组utf8 字段是该字节的 utf-8表示 ,这是一个 lazily computed 字段,即只有真正需要获取(也仅在第一次,因为byteString的设计是不可变的)才会去做转换。 ByteString 是思想是 以空间换时间来提高时间上的效率。

  ByteString 的构造函数并没有 clone data,因为这个类内部本身并不会对 data做任何的修改,既然是immutable,也就没必要浪费这个空间了。

  ByteString(byte[] data) {
    this.data = data; // Trusted internal constructor doesn't clone data.
  }

ByteString 提供了很多静态帮助方法 类型包括 数据的加密(包括 md5、sha1、等)、数据的截取、大小写转换、数据IO操作等,这些方法的返回都是一个新的 ByteString,这也是 immutable 的弊端,在复杂操作过程中会创建大量的对象。

TimeOut源码分析

见我的TimeOut源码分析

用例

  官网上读取 png 图片的例子

private static final ByteString PNG_HEADER = ByteString.decodeHex("89504e470d0a1a0a");

public void decodePng(InputStream in) throws IOException {
  BufferedSource pngSource = Okio.buffer(Okio.source(in));//inputstream 转化成okio的 Source

  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;
  }

  pngSource.close();
}

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);
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Okio是一个强大的Java I/O库,用于处理输入和输出流的操作。要下载Okio的jar文件,可以按照以下步骤进行: 1. 在网上的Maven仓库或者其它可靠的软件下载网站上搜索"Okio jar"。 2. 找到适合你的项目的Okio jar文件版本。可以根据你的项目需求选择稳定版本或者最新版本。 3. 点击下载链接并选择合适的下载目录。 4. 下载完成后,将jar文件移动到你的项目目录中的lib文件夹(如果没有lib文件夹,则可以创建一个新的lib文件夹)。 5. 打开你的IDE(如Eclipse、IntelliJ IDEA等),导入下载好的Okio jar文件到你的项目中。 6. 在你的代码中导入Okio库的类或者方法,你就可以开始使用Okio库了。 这些步骤将帮助你下载并且集成Okio jar到你的项目中,让你能够使用Okio库的强大功能。请确保下载和使用的是可靠的版本,并充分理解Okio库的使用方式和文档,以便更好地应用到你的项目中。 ### 回答2: OKIO是一个开的轻量级IO库,用于在Java平台上进行高效的IO操作。你可以通过下载OKIO jar包来使用它。 首先,你需要找到OKIO的官方网站或是在Maven中央仓库中搜索OKIO。官方网站通常会提供OKIO的jar包下载链接。 一旦找到下载链接,你可以点击下载按钮来获取OKIO的jar包。下载完成后,你将得到一个以.jar结尾的文件。 接下来,将下载的jar包移动到你的Java项目中合适的位置。通常情况下,你可以将它放在项目的lib目录中。 然后,在你的项目中配置构建工具(例如Maven或Gradle)以引入OKIO的依赖。你可以在构建工具的配置文件中添加OKIO的依赖信息,例如Maven的pom.xml文件或Gradle的build.gradle文件。 在配置文件中添加OKIO的依赖信息后,保存文件并进行构建。构建工具将会自动下载并引入OKIO的jar包到你的项目中。 最后,你可以在你的Java代码中使用OKIO库了。可以通过导入OKIO的类来使用它提供的功能,例如读取和写入文件、缓冲区操作等。 总结来说,OKIO的jar包可以通过下载官方网站提供的链接或在构建工具中引入依赖来获取。下载完成后,将jar包放置在项目中合适的位置,并在配置文件中添加依赖信息。完成这些步骤后,你就可以在你的项目中使用OKIO库了。 ### 回答3: Okio是一个高效的Java I/O库,主要用于处理流和字节。要下载Okio JAR文件,可以按照以下步骤进行操作: 1. 打开浏览器并前往Maven仓库的网站(https://mvnrepository.com/)。 2. 在搜索框中输入“Okio”并点击搜索按钮。 3. 在搜索结果中找到最新版本的Okio库,通常以“okio”开头。单击库的版本号以进入详细信息页面。 4. 在详细信息页面中,您将看到有关该库的信息,包括依赖项和Gradle / Maven坐标。 5. 在坐标部分,您可以找到Gradle和Maven的引用代码。根据您的项目构建工具选择适合您的代码。 - 如果您使用Gradle构建项目,请将Gradle代码复制到项目的build.gradle文件中的dependencies部分。 - 如果您使用Maven构建项目,请将Maven代码复制到项目的pom.xml文件中的dependencies部分。 6. 复制引用代码后,保存并关闭文件。 7. 重新构建和编译项目,您的项目将自动下载并使用Okio JAR文件。 请注意,确保您的网络连接良好,以便从Maven仓库成功下载JAR文件。另外,如果您使用的是集成开发环境(IDE),如IntelliJ IDEA或Eclipse,您可以使用IDE的依赖管理工具来搜索并添加Okio库,这更加方便。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卓修武

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值