5

为你推荐一款高效的IO组件——okio-开源基础软件社区-51CTO.COM

 2 years ago
source link: https://ost.51cto.com/posts/13672
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

为你推荐一款高效的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信息,将其添加到输出流缓冲区的双向链表的尾部,省去一次数据复制过程。

为你推荐一款高效的IO组件——okio-开源基础软件社区

图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的数据写入输出流缓冲区。

为你推荐一款高效的IO组件——okio-开源基础软件社区

图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:文件写入和读取

为你推荐一款高效的IO组件——okio-开源基础软件社区

本示例通过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解码

为你推荐一款高效的IO组件——okio-开源基础软件社区

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

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

示例3:十六进制解码

为你推荐一款高效的IO组件——okio-开源基础软件社区

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

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

示例4:Utf8编码

为你推荐一款高效的IO组件——okio-开源基础软件社区

本示例通过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

为你推荐一款高效的IO组件——okio-开源基础软件社区

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK