写点什么

为你推荐一款高效的 IO 组件——okio

  • 2022 年 6 月 10 日
  • 本文字数:3888 字

    阅读完需:约 13 分钟

为你推荐一款高效的IO组件——okio

前不久,三方组件库上新了一批 JS/eTS 组件,其中就包括 okio 组件。okio 是一个可应用于 HarmonyOS 的高效 IO 库,它依托于系统能力,提供字符串的编解码转换能力,基础数据类型的读写能力以及对文件读写的支持。本期将为大家介绍 okio 的工作原理及使用方法。

一、okio 的产生背景

IO,即输入输出(Input/Output)。绝大多数应用都需要与外部进行数据交互,这就会涉及 IO。系统提供了 IO 能力,在使用系统 IO 时,通常需要一个中间缓冲区来保存读取到的数据。数据先从输入流缓冲区复制到中间缓冲区,再从中间缓冲区复制到输出流缓冲区。中间多次拷贝,降低了 IO 效率,同时增加了系统消耗。为了满足开发者对 IO 的更高要求,三方组件库推出 IO 处理利器——okio(JS 版本)。okio 使用 Segment 作为数据存储容器,通过提供 Segment 移动、共享、合并和分割的能力,让数据读写变得非常灵活,也减少了数据复制,提升了 IO 效率。此外,okio 还通过 SegmentPool 对 Segment 进行回收和复用,减少大量创建 Segment 带来的系统消耗。下面就带大家深入了解 JS 版本的 okio 的工作原理,探索它是如何提升 IO 效率的~

二、两个基本概念

在深入解析 okio 的工作原理之前,我们先来了解两个基本概念:Segment 和 SegmentPool。


1. Segment

okio 将数据分割成一块块的片段存放在 Segment 里面。Segment 是一个数据存储的真正类,内部维护着一个大小为 8192 字节的字节数组用于存储数据。Segment 最小可共享、可写入的数据大小为 1024 字节。Segment 使用 pos、limit、shared、owner、prev、next 来分别记录读写位置、是否可写入、是否能共享、数据拥有者、前置节点和后置节点信息。Segment 对外提供 sharedCopy、unsharedCopy、split、push、pop、compact、writeTo 等接口用于操作数据。


Segment 同时拥有前置节点和后置节点,构成一个双向链表。读取数据的时候,从双向链表的头部开始读取;而写入数据的时候,从双向链表的尾部写入数据。


2. SegmentPool

为了管理 Segment,okio 维护了一个 Segment 对象池(即 SegmentPool),对废弃的 Segment 回收、复用和内存共享,从而减少内存的申请和 GC(garbage collection,垃圾收集)的频率,使性能得到优化。SegmentPool 是一个由最多 8 个 Segment 组成的单链表。一个 Segment 的最大大小是 8192 字节(即 8KB),所以 SegmentPool 的最大大小是 64KB。

三、okio 的工作原理

okio 组件最重要的功能就是“读”和“写”。下面我们就从读写开始,了解 okio 的工作原理。


1. 读写数据

okio 读写数据的过程中,遵循大块数据移动、小块数据复制的原则。okio 从输入流读取数据到输入流缓冲区时,会先找到双向链表尾部的 Segment 节点,如果此节点的剩余容量足够,则直接将读取到的数据存入到此节点。如果此节点的剩余容量不足,则从 SegmentPool 里面取一个 Segment 链接到双向链表的尾部,然后将数据存入这个新节点。okio 从输入流缓冲区读取数据,再写入数据到输出流缓冲区。这个过程比较复杂,有以下几种情况:


(1) 从输入流缓冲区获取到 Segment,如果数据是满的(字节数组 data 长度为 8092 字节),那么直接修改此 Segment 的 prev 和 next 信息,将其添加到输出流缓冲区的双向链表的尾部,省去一次数据复制过程。


图 1 大块数据移动


(2) 从输入流缓冲区获取到 Segment(假设为 Segment1),如果数据不是满的,可以通过 pos 和 limit 信息来确定 segment1 的可读数据,再和输出流缓冲区的双向链表的尾部节点(假设为 Segment2)的剩余容量进行对比:如果 Segment1 的可读数据比 Segment2 的剩余容量小,则把 Segment1 的数据复制到 Segment2,然后回收 Segment1 到 SegmentPool。如果 Segment1 的可读数据比 Segment2 的剩余容量大,那么直接修改 Segment1 的 prev 和 next 信息,将其添加到 Segment2 的后面。


(3) 从输入流缓冲区获取到 Segment(假设为 Segment3),如果只需要传递部分数据(比如总数据为 4096 字节,只传递 1024 字节),okio 会通过 split 接口将 Segment3 拆分成含 3072 字节数据的 Segment3-1 和含 1024 字节数据的 Segment3-2,然后按照(2)的逻辑将 Segment3-2 的数据写入输出流缓冲区。


图 2 Segment 拆分


拆分 Segment 的时候,可以通过参数指定拆分后的第一个 Segment 含有的未读字节数(byteCount)。拆分后,第一个 Segment 包含的数据范围是[pos,pos+byteCount),第二个 Segment 包含的数据范围是[pos+byteCount,limit)。拆分 Segment 时也遵循大块数据移动、小块数据复制的原则。当 byteCount 大于 1024 时,使用共享的 Segment,否则复制数据。(注:文件、流、socket 相关的 IO 优化需要系统支持,待后续版本优化提供。)


2. Segment 的回收与复用

接下来,我们再来看看 SegmentPool 是如何回收和复用 Segment 的。


每次 okio 想要使用 Segment 就从 SegmentPool 中获取,使用完毕后又会放回到 SegmentPool 中等待复用,核心方法为 take()和 recycle()。


(1) take()方法

take()方法负责从对象池单链表的头部获取可以使用的 Segment。如果获取不到,说明单链表是空的,此时新创建一个 Segment 给缓冲区使用。如果能获取到,则取出单链表的头部节点,再将下一个节点置为单链表的头部节点,并将取出来的 Segment 的 next 置空,同时更新对象池大小。


(2) recycle()方法

recycle()方法负责回收缓冲区里面使用完毕的 Segment。回收开始时,首先更新对象池大小,然后把回收对象 Segment 添加到单链表头部,接着重置 Segment 的 pos 和 limit 为 0。注意,以下情况不会回收 Segment:

  • 当前 Segment 的 prev 和 next 不为空

  • 当前 Segment 是共享的

  • 对象池已经有 8 个 Segment 了


3. 字符串处理

除了 Segment 和 SegmentPool 外,okio 还封装了 ByteString 类来进行字符串处理。ByteString 提供 Base64 编解码、utf-8 编码、十六进制编解码、大小写转换、内容比较等丰富的 API,可以很方便地处理字符串。在进行字符串处理时,由于 ByteString 同时持有原始字符串和对应的字节数组,可以直接使用字节数组里面的数据进行操作,不需要先将字符串转换为字节数组。特别是在频繁转换编码的场景下,通过这种以空间换时间的方式,可以避免字符串与字节数组的多次转换,减少了时间和系统性能消耗。

四、okio 的使用及示例

1. 前置配置步骤一:在 entry 的 package.json 文件中添加以下依赖项。

"dependencies": {    "okio": "^1.0.0"    }
复制代码


步骤二:配置仓库镜像地址。

npm config set @ohos:registry=https://repo.harmonyos.com/npm/
复制代码


步骤三:DevEco Studio 的 Terminal 里面输入以下命令下载源代码。

cd entrynpm install @ohos/okio
复制代码


步骤四:文件的头部引入 okio 库。

 import okio from '@ohos/okio';
复制代码


步骤五:在 config.json 文件中申请存储权限。

   "reqPermissions": [            {                "name": "ohos.permission.WRITE_USER_STORAGE", //写入用户存储的权限                "reason": "Storage",                "usedScene": {                    "when": "always",                    "ability": [                       "com.example.okioapplication.MainAbility"                   ]                }            },           {                "name": "ohos.permission.READ_USER_STORAGE", //读取用户存储的权限                "reason": "Storage",                "usedScene": {                    "when": "always",                    "ability": [                        "com.example.okioapplication.MainAbility"                    ]                }            },            {                "name": "ohos.permission.WRITE_EXTERNAL_MEDIA_MEMORY", //写入外部存储的权限        "reason": "Storage",                "usedScene": {                    "when": "always",                    "ability": [                        "com.example.okioapplication.MainAbility"                    ]                }            }        ]       }
复制代码


2. 代码实现执行完上面的配置操作后,就可以进入代码编写阶段了。开发者可以使用 okio 提供的丰富的 API 接口来实现功能。下面为大家展示四个实现示例,供大家参考学习。


示例 1:文件写入和读取


本示例通过 sink 将内容写入文件,通过 source 从文件读取内容。代码如下:

//通过sink将内容写入文件var sink = new okio.Sink(this.fileUri);sink.write(this.Value,false); //通过source从文件读取内容var source = new okio.Source(this.fileUri);source.read().then(function (data) { context.readValue = data;       }).catch(function (error) {console.log("error=>"+error);       });
复制代码


示例 2:Base64 解码


本示例通过 ByteString 实现 Base64 解码功能,代码如下:

let byteStringObj = new okio.ByteString.ByteString(''); //生成ByteString对象let decodeBase64 = byteStringObj.decodeBase64('SGVsbG8gd29ybGQ='); //解码Base64字符串this.decodeBase64Value = JSON.stringify(decodeBase64); //显示解码结果
复制代码


示例 3:十六进制解码


本示例通过 ByteString 实现十六进制解码功能,代码如下:

let byteStringObj = new okio.ByteString.ByteString('');let decodehex = byteStringObj.decodeHex('48656C6C6F20776F726C640D0A');this.decodeHexValue = JSON.stringify(decodehex);
复制代码


示例 4:Utf8 编码


本示例通过 ByteString 实现 Utf8 编码功能,代码如下:

let byteStringObj = new okio.ByteString.ByteString('');let encodeUtf8 = byteStringObj.encodeUtf8('Hello world #4 ❤ ( ͡ㆆ ͜ʖ ͡ㆆ)');this.encodeUtf8Value = JSON.stringify(encodeUtf8);
复制代码


本期 okio 组件就为大家介绍到这里了。okio 组件已开源,欢迎大家参与贡献。

开源地址如下:

https://gitee.com/openharmony-tpc/okio



用户头像

每一位开发者都是华为要汇聚的星星之火 2021.10.15 加入

提供HarmonyOS关键技术解析、版本更新、开发者实践和活动资讯,欢迎各位开发者加入HarmonyOS生态,一起创造无限可能!

评论

发布
暂无评论
为你推荐一款高效的IO组件——okio_HarmonyOS_HarmonyOS开发者社区_InfoQ写作社区