原文:https://mp.weixin.qq.com/s/XnNhSq8ESoslb2DQEzMbCQ,点击链接查看更多技术内容。
 
前不久,三方组件库上新了一批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组件已开源,欢迎大家参与贡献。
 
开源地址如下:
 

为你推荐一款高效的IO组件——okio的更多相关文章

  1. 推荐一款java的验证码组件——kaptcha

    使用方法: 项目中导入kaptcha-2.3.jar包 在web.xml里面新增:   <!-- 登陆验证码Kaptcha --> <servlet> <servlet- ...

  2. 软件推荐 ---一款优秀的通信组件 HP_Socket

    * HP-Socket 官方网站:http://www.jessma.org* HP-Socket 项目主页:http://www.oschina.net/p/hp-socket* HP-Socket ...

  3. 推荐几款我一直在用的chrome插件(下)

    请先看:推荐几款我一直在用的chrome插件(上) 6. Pocket 可以很方便的保存文章.视频等供以后查看,即实现了“Read it later”功能.有了 Pocket,您可以将所有想下次读的内 ...

  4. 推荐15款最好的 Twitter Bootstrap 开发工具

    Twitter Bootstrap 自从2011年最初发布到网上后,迅速成为 Web 领域最流行的响应式前端开发框架之一,是网页设计的优秀实践.Twitter Bootstrap 框架包含了众多的预定 ...

  5. 向 Web 开发人员推荐35款 JavaScript 图形图表库

    图表是数据图形化的表示,通过形象的图表来展示数据,比如条形图,折线图,饼图等等.可视化图表可以帮助开发者更容易理解复杂的数据,提高生产的效率和 Web 应用和项目的可靠性. 在这篇文章中,我们收集了3 ...

  6. 值得拥有!精心推荐几款超实用的 CSS 开发工具

    当你开发一个网站或 Web 应用程序的时候,有合适的工具,绝对可以帮助您节省大量的时间.在这篇文章中,我为大家收集了超有用的 CSS 开发工具. 对于 Web 开发人员来说,找到有用的 CSS 开发工 ...

  7. 推荐一款超强大的基于Angularjs的自动完成(Autocomplete)标签及标签组插件–ngTagsInput

    前言 今天利用中午午休时间,给大家分享推荐一款基于Angularjs的自动完成(Autocomplete)标签及标签组插件--ngTagsInput,功能超强大的.不信,你试试就知道^_^... Au ...

  8. 推荐25款php中非常有用的类库

    推荐25款php中非常有用的类库 投稿:hebedich 字体:[增加 减小] 类型:转载 时间:2014-09-29   作为一个PHP开发者,现在是一个令人激动的时刻.每天有许许多多有用的库分发出 ...

  9. 推荐10款非常优秀的 HTML5 开发工具

      HTML5 发展如火如荼,随着各大浏览器对 HTML5 技术支持的不断完善以及 HTML5 技术的不断成熟,未来 HTML5 必将改变我们创建 Web 应用程序的方式.今天这篇文章向大家推荐10款 ...

  10. 推荐一款接口 API 设计神器!

    今天栈长给大家推荐一款接口 API 设计神器,传说中的,牛逼哄洪的 Swagger,它到底是什么?今天为大家揭开谜底! Swagger是什么? 官网:https://swagger.io/ Swagg ...

随机推荐

  1. gorm整理

    目录 1. 约定 2. 结构体标签 3. 创建记录 4. 更新 5.删除 6. 查询 7.关联 8.链式操作 9.范围 10.多个立即执行方法的注意事项 11.错误处理 12.钩子 13.事务 14. ...

  2. 使用LabVIEW打开默认应用程序中的文档(PDF,Word,Excel,Html)

    问题详情 我想让我的LabVIEW VI使用默认应用程序打开硬盘上的文档.如何实现? 解决方案 有一个名为 "Open a Document on Disk.vi" 的 VI,它可 ...

  3. 1 - RTOS简介&规范&任务创建

    对外部响应能力: 实时(规定时间内)操作系统 , 有强(飞机系统)弱(信息采集系统)之分 与分时(顺序时间片)操作系统 FreeRTO实时操作系统 支持抢占式调度,合作式调度和时间片调度:内核大小在4 ...

  4. SyntaxError: invalid property id(就是不支持ES6) (浏览器不支持对象...展开)

    SyntaxError: invalid property id(就是不支持ES6) (浏览器不支持对象...展开) 火狐55以后支持

  5. buildFast.js node.js 快速发布到gitee上,这样就不用每次点击,并且自动弹出发布页面,再点击发布,完美!

    buildFast.js node.js 快速发布到gitee上,这样就不用每次点击,并且自动弹出发布页面,再点击发布,完美! const fs = require('fs-extra'); fs.c ...

  6. 表单验证 validate 两种 一种是callback配合外部变量,当同步用。第2中是 then async await 这种 真正$api也适用

    validateFormRealProcessLeft () { let ret = false this.$refs.xxx.validate((valid) => { if (valid) ...

  7. Python根据时间命名并创建文件源码

    自己写的,产品中验证ok的代码,直接上实例: import time def file_create_func(): loca = time.strftime('%Y-%m-%d-%H-%M-%S') ...

  8. 手爱图仕Aputure MC RGB摄影补光灯体验测评

    前记  作为一个智能硬件设备狂兼智能硬件方案提供商,工作就是和各种智能硬件打交道.最近无意入手了一个爱图仕Aputure MC RGB摄影补光灯.顺便体验了几天,既然体验了,肯定要写一个体验结果的.这 ...

  9. day02-显示所有菜品&点餐功能

    满汉楼02 4.功能实现04 4.6显示所有菜品 4.6.1思路分析 创建一个菜单表menu,在Domain层创建与菜单表对应的Javabean-Menu类,在DAO层创建MenuDAO,完成对men ...

  10. (模板)Manacher算法:线性时间求字符串内回文子串的数量

    已通过leetcode647:https://leetcode-cn.com/problems/palindromic-substrings/ void get_d(vector<int> ...