OKIO源码分析

OKIO源码分析

概述

从okio在github上的README我们知道okio主要有四个东西,分别是Sink, Source,Buffer,ByteString
Sink:类似java中输出流OutputStream
- Source:类似java中的输入流InputStream
- Buffer:看名字就知道就是一个缓冲区,看到这个Buffer 是不是想起了我们平时写io流时都会创建一个byte[] 作为缓存区,写入时,将这个缓存区的数据写入流,读取时,将流中的数据读入这个缓存区。往下看就会知道Buffer 的内部其实就是byte 数组。
- ByteString:顾名思义,这个类跟byteString 有关。

Sink 和 Source

Sink, Source 都是接口,定义了一些行为,供子类实现。
这里写图片描述
可以看到它们的内部定义了非常简单的方法。也可以看出它们必须配合Buffer 来使用才行。
BufferSink 也是接口,继承了Sink 并提供了更多的write 方法使其能处理byte, short, String 等类型,同理BufferSource 也是接口,继承了Source 并提供了更多的read 方法。

Buffer

接下来就来看看实现了BufferSink和BufferSource 的类RealBufferdSink, RealBufferdSource。这两个类的内部write 方法或者 read 方法的实现都是通过Buffer 类,重头戏来了。
这里写图片描述
Buffer 类实现了BufferedSource, BufferdSink, Cloneable 接口,具有两个属性long size和 Segment headlong size 表示其内部所存储的数据的长度,它存储的数据其实本质上就是byte[]Segment 内部有一个大小为2048的byte[] 保存着Buffer 需要存储的数据。

Buffer 只能存储2048byte 么? 当然不是,Segment 的实现方式是一个循环双线链表,当一个Segment 存满了,往链尾添加一个新的Segment 就又可以存储更多的数据了。

那为什么不直接在Buffer 中写一个byte[] 来保存数据,还要弄一个什么循环链表Segment 来存储数据呢?答案就在数据的转移。当一个Source 要读取一个Sink 中的数据时,其实就是把数据从SourceBuffer 转移到SinkBuffer 中。此时如果Buffer 中是用byte[] 实现的,那么我们势必需要进行byte 数组拷贝的工作,但是用一个链表实现的情况下,我们可以直接把Sink 中的 Segment 节点的指针指到Source 中的Segment 链表尾就OK了,而不用进行数组拷贝,更高效。

Segment

上面收到Segment 是链表的实现方式, 分析该类就从Buffer 的方法wite(byte[] source, int offset, int byteCount) 开始吧。

@Override public Buffer write(byte[] source, int offset, int byteCount) {
    if (source == null) throw new IllegalArgumentException("source == null");
    checkOffsetAndCount(source.length, offset, byteCount);

    int limit = offset + byteCount;
    while (offset < limit) {
      Segment tail = writableSegment(1);

      int toCopy = Math.min(limit - offset, Segment.SIZE - tail.limit);
      System.arraycopy(source, offset, tail.data, tail.limit, toCopy);

      offset += toCopy;
      tail.limit += toCopy;
    }

    size += byteCount;
    return this;
  }

checkOffsetAndCount 方法就是检查下参数对不对。检查完下面有个while 循环将数据拷贝到Segment中,那该Segment 是怎么来的呢,我们看下writableSegment(int minmumCapacity) 方法。

Segment writableSegment(int minimumCapacity) {
    if (minimumCapacity < 1 || minimumCapacity > Segment.SIZE) throw new IllegalArgumentException();

    if (head == null) {
      head = SegmentPool.take(); // Acquire a first segment.
      return head.next = head.prev = head;
    }

    Segment tail = head.prev;
    if (tail.limit + minimumCapacity > Segment.SIZE || !tail.owner) {
      tail = tail.push(SegmentPool.take()); // Append a new empty segment to fill up.
    }
    return tail;
  }

该方法根据你提供的容量返回一个能存的下你声明的容量的Segment 当作容器供你使用。当Buffer 的内部没有存储数据时,它会调用SegmentPool.take() 方法获取到一个SegmentSegmentPool 是一个Segment 的缓存池, 最多可以缓存64 * 1024KiB也就是32个Segment 。这种缓冲方式跟Message 很像。Buffer 的内部有存储数据时会先查看链尾节点的存储空间符不符合你的要求,符合就返回,不符合就调用Segment.push(Segment segment) 方法添加一个新的节点在末尾供你存储数据。 拿到Segment 容器后,就可以拷贝数据了,如此反复直到数据都写入到Buffer 中。

ForwardingSink、 ForwardingSource

这两个类网络上有人说它们是装饰着 但是我觉得它们更像是代理,用法就是继承它们,然后override它们的方法。

GzipSink、 GzipSource、 DeflaterSink、 InflaterSource

GzipSink和GzipSource 实现了gzip 压缩,解压缩。其实就是弄个头,体,尾。头跟尾详情见GZIP。 体的话是调用DeflaterSink跟InflaterSource 实现的。而这两个类的内部又是调用java的API Deflater和Inflater 类实现压缩和解压的。关于这两个类的使用方式,不必google了。直接看这两个的头部注释就够了,在这里我也贴出来吧。

Inflater用法

 byte[] compressedBytes = ...
     int decompressedByteCount = ... // From your format's metadata.
     Inflater inflater = new Inflater();
     inflater.setInput(compressedBytes, 0, compressedBytes.length);
     byte[] decompressedBytes = new byte[decompressedByteCount];
     if (inflater.inflate(decompressedBytes) != decompressedByteCount) {
         throw new AssertionError();
     }
     inflater.end();

Deflater用法

byte[] originalBytes = ...

     Deflater deflater = new Deflater();
     deflater.setInput(originalBytes);
     deflater.finish();

     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     byte[] buf = new byte[8192];
     while (!deflater.finished()) {
         int byteCount = deflater.deflate(buf);
         baos.write(buf, 0, byteCount);
     }
     deflater.end();

     byte[] compressedBytes = baos.toByteArray();

ByteString

说了半天还没有说ByteString 呢,看到这个名字就知道跟ByteString 有关。内部有一个byte[] 用来保存数据, 主要的作用就是将字符串转成byte[] 保存起来,并提供将byte[] 转成字符串的方法,也提供了一些将字符串加密编码的方法。

Okio

最后说下最常用的大Boss, Okio 类。我们使用Okio库都要用到这个类,该类是一个大工厂, 为我们创建出各种各样的SinkSource 对象。提供了三种数据源InputStream/OutputStreamSocketFile, 我们可以把本应该对这三类数据源的IO操作通过Okio库来实现,更方便,更高效。

说到这里也就差不多了,还有不明白的,还是去看下源码,也不多,也不难!

### 回答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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值