前不久,三方组件库上新了一批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信息,将其增加到输入流缓冲区的双向链表的尾部,省去一次数据复制过程。
(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的数据写入输入流缓冲区。
拆分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" ] } } ] }
代码实现执行完下面的配置操作后,就能够进入代码编写阶段了。开发者能够应用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...