鸿蒙NEXT API 使用指导
- 一、前言
- 二、邮件创建
- 1、拉起垂类应用
- 2、 UIAbilityContext.startAbilityByType 原型
- 2.1、wantParam
- 2.2、abilityStartCallback 与 callback
- 3、拉起邮箱类应用
- 3.1、单纯拉起邮箱应用
- 3.2、传入带附件的邮件
- 三、压缩文件
- 1、认识 zlib
- 2、压缩处理
- 2.1、单文件压缩
- 2.2、多文件压缩
- 3、解压缩处理
- 4、获取文件信息
一、前言
上一篇,由于篇幅所限,并未对实现问题反馈功能所使用的两个核心系统 API,进行较为细致的使用说明,更为提及使用它们时应该注意的地方,为了避免初学者掉坑,本人特意撰写本篇文章进行补充。
二、邮件创建
邮件,对于普通人来说,或许是一个知道但用得不多的通讯工具,然而,在程序员群体,邮件确实使用频率相当高的,因为众多代码托管平台或开发者平台,都会基于邮箱进行账号注册,并且邮箱这种工具在联系相关开发者时,要比流行的即时通讯工具更为有效得多,毕竟很容易集成到开发者自己开发的相关系统平台中。
1、拉起垂类应用
言归正传,在鸿蒙 NEXT 应用开发框架中中,如果像拉起手机系统已安装的邮箱应用,并且传入一封内容填写好的邮件,是完全有现成 API 可以使用的,就是 UIAbilityContext.startAbilityByType
和UIExtensionContentSession.startAbilityByType
,想对来说,前者的使用更为简便,而在实现 TxtEdit 的问题反馈功能时,也是用了它,所以,本文重点了解前者,后者还请移步官方文档。
在 API13 版本中,UIAbilityContext.startAbilityByType 支持拉起的应用类型有如下:
因此,想要拉起邮箱类应用,只需将 type 参数设置为 mail 即可,具体来说就是形如下面的代码:
this.ctx.startAbilityByType("mail", wantParam, callback,
(err) => { ... });
2、 UIAbilityContext.startAbilityByType 原型
UIAbilityContext.startAbilityByType 方法的声明原型如下:
有四个必传参数,并且第一个参数就是指定应用类型的参数。按照参数的声明顺序,下面我们了解一下剩余的三个参数的传值。
2.1、wantParam
在 UIAbilityContext.startAbilityByType 方法的第二个参数 wantParam,是一个 Record<string, Object>
类型,作用是设置拉起邮箱应用时需要一并传入的数据,针对 type=mail 时,这个 wantParam 可以传入的合法参数选项有如下:
都不是必填的,有两个需要注意的细节:
1) string 类型或 string[] 类型的数据值,都必须用encodeURI
编码一下;
2)邮箱地址,如收件人地址,在仅需填写一个邮箱地址的时候,如果错误地设置成 string 类型,即没有携带上中括号,那么就会出现能够成功拉起邮箱应用,但不会进入邮件编辑界面,也不会有任何错误抛出。
wantConstantFlags 的枚举值有如下几个:
这个 wantConstantFlags 选项,在需要创建带附件的邮件时,相当重要,设置错误将导致无法读取文件内容到邮件中。
2.2、abilityStartCallback 与 callback
abilityStartCallback 是一个 AbilityStartCallback 类型的参数,在 UIAbilityContext.startAbilityByType 中的作用,就是提供获取执行结果道德入口
。
callback 则是一个 AsyncCallback<void>
,为异步回调,用于获取接口调用是否成功。
这两个回调函数参数的区别,就在于 abilityStartCallback 是任务粒度的,而 callback 是接口粒度的,如此设计,是因为接口调用是否成功,与任务执行是否成功,是两码事,任务执行失败而接口调用不一定失败,但接口调用失败则任务执行一定失败。
3、拉起邮箱类应用
3.1、单纯拉起邮箱应用
如果不需要在拉起邮箱应用时,传入一封邮件,那么,wantParam 应该怎么传参呢?
思考一下,前面提到的针对邮箱类应用的 wantParam 选项,为什么都设置成非必填呢?如果传入一个空值的 wantParam 去拉取邮箱类应用,会有什么样的执行结果呢?想要知道会发生什么,不妨写段代码在真机上跑一下:
let callback: common.AbilityStartCallback = {
onError: (code: number, name: string, message: string) => {
Logger.error(`拉起邮箱失败,错误码:${code}, 错误名:${name}, 错误信息:${message}`, TAG)
promptAction.showToast({
message: "调用邮箱失败,请稍后重试",
duration: 3000
})
},
onResult: (result) => {
Logger.info(`拉起邮箱成功,结果: ${JSON.stringify(result)}`, TAG)
promptAction.showToast({
message: '调用邮箱成功',
duration: 3000
})
}
};
this.ctx.startAbilityByType("mail", {}, callback, (err) => {
if (err) {
Logger.error(`startAbilityByType调用失败,错误信息:${err.message}`, TAG)
}
Logger.info(`startAbilityByType调用成功`, TAG)
})
执行之后,首先会弹出应用选择面板,选择目标邮箱后,会进入目标邮箱应用并跳转到邮件新建界面。
这显然与我们想要的单纯进入邮箱应用主页,不进入邮件编辑页面,并不一致,为了达到这个目的,就应当从空传的 wantParam入手了;还记得前面介绍 wantParam 合法选项时,提到注意细节吗?其中就有一个强调邮箱地址要用 string[] 类型透传,但如果我们有意传成 string 类型,就如下面的代码这般:
const wantParam: Record<string, Object> = {
'sceneType': 1,
'email': '15014366986@163.com', // 收件人邮箱地址
};
this.ctx.startAbilityByType("mail", wantParam, callback, (err) => {
if (err) {
Logger.error(`startAbilityByType调用失败,错误信息:${err.message}`, TAG)
}
Logger.info(`startAbilityByType调用成功`, TAG)
})
就能实现我们的目的,不过,显而易见,这种实现方式需要慎重,因为后续的 API 迭代中,可能会对 wantParam 中的选项的类型进行强校验,校验不通过就抛错。
3.2、传入带附件的邮件
利用 wantParam,我们可以实现调用邮箱应用并进入编辑界面,并且提前填写好相关内容,无需用户自己手动输入信息,比如利用下面的 wantParam 传入带附件的邮件内容:
const wantParam: Record<string, Object> = {
'sceneType': 1,
'email': [encodeURI("wy1589461@163.com")], // 收件人邮箱地址,多值以逗号分隔,对数组内容使用encodeURI()方法进行url编码
'subject': encodeURI('TxtEdit用户使用问题反馈'), // 邮件主题,对内容使用encodeURI()方法进行url编码
'body': encodeURI(mailBody), // 邮件正文,对内容使用encodeURI()方法进行url编码
'ability.params.stream': [encodeURI(uri)], // 附件uri,多值以逗号分隔,对数组内容使用encodeURI()方法进行url编码
'ability.want.params.uriPermissionFlag': wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION
};
这里给出的案例,显然是主送一人、无抄送和密送的情况,如果需要抄送和密送,那么就需要为 cc
选项和 bcc
选项设置邮箱地址;邮件要携带的附件,以[encodeURI(文件URI)]
的形式,为选项 ability.params.stream 设置值,并且为了保证有权限去读取目标文件中的内容,ability.want.params.uriPermissionFlag 必须设置成 wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION
。
三、压缩文件
从 ability.params.stream
接受 string[]
类型的数据值来看,邮件附件是支持同时携带多份文件的,但有时候为了尽可能减小邮件的体积、降低对带宽的占用和提供传输效率,将多份需要邮件发送的文件压缩成一个压缩包进行发送,是常用的操作。
除了邮件,很多网络传输文件场景中,也比较推荐以压缩包的形式,去传输多份文件,同时,大文件的网络传输,用压缩包的形式进行传输,可以极大的提高成功率。
在移动端,处理压缩包的情况比较少见,但这并不意味着手机操作系统就没有相应的功能,实际上,安卓系统和鸿蒙系统都支持打开、解压缩一定格式的压缩包文件,而目前使用量最大的鸿蒙4.x版本的系统中,用手机管家清理加速的时候,是能够对不常用应用和文件进行压缩处理、从而降低对存储空间的占用。
1、认识 zlib
在鸿蒙NEXT 开发框架中,有一个 @ohos.zlib
模块,该模块下提供用于文件压缩与解压缩的相关API,并且开箱可用,主要支持 .zip
和 .gzip
两种压缩包文件格式。
所有 API 可以大致划分为压缩处理、解压缩处理和获取文件信息三大类。
2、压缩处理
遵循先有鸡后有蛋的原则,我们先看压缩处理相关的 API。
通过归纳官方文档,梳理出关于文件压缩处理的 API,主要有:
1)zlib.compressFile 与 zlib.compressFiles
2) Checksum.adler32 与 Checksum.adler32Combine、Checksum.crc32与Checksum.crc32Combine、Checksum.crc64
3)Zip.compress 与 Zip.compressBound,Zip.deflateInit 与 Zip.deflateInit2,Zip.deflate 等 Zip.deflateXXXX
4)Gzip.write
从使用难易的角度来说,第一组是最容易使用的,所以 ,本文重点介绍,后面的几组供学有余力的读者,前往官方文档仔细摸索。
2.1、单文件压缩
开始介绍前,给大家普及一个知识点,在计算机操作系统中,目录也是文件,一种格式比较特殊的文件。
单文件压缩,可以使用zlib.compressFile
,这里传授大家一个经验,就是阅读API文档或者在代码提示中选择API时,一定要留意API名称的单复数,单数形式命名的API,往往只能一次处理一份数据,而复数形式命名的API 则可以一次处理多份数据,举一反三,大家在封装自己的工具类的时候,也应该遵循这种规则。
zlib.compressFile 一共如下四个参数:
1)inFile 是待压缩的文件或目录,不论是文件还是目录,对应的路径都必须是沙箱路径,并且如果是文件夹,那么该文件夹应为非空文件夹,否则会引发解压缩操作报错。
2)outFile 是保存压缩数据的压缩包文件路径,注意文件名取名为xxx.zip
3)options,压缩配置相关的选项:
a、level,压缩等级,合法枚举值如下:
b、memLevel,压缩过程中内存使用程度
c、strategy,压缩策略
2.2、多文件压缩
压缩多文件时,虽然可以选择将整个目录进行压缩,然而,有时候,需要压缩的目标文件,并不是所在目录下的所有文件,仅仅是其中的几个,针对这种应用场景,对多个指定文件或指定目录进行压缩的操作,就必须支持,而鸿蒙 NEXT 开发框架,通过 zlib.compressFiles 进行支持。
zlib.compressFiles 的方法参数,相对于前面的单文件压缩方法,少了一个回调函数参数,并且第一个参数更名为 inFiles、使用 Array<string>
类型
3、解压缩处理
zlib 模块中,针对解压缩处理,同样提供了多组 API,但本文为了内容的相关性,这一节只介绍 zlib.decompressFile 的使用。
就解压缩来说,并不存在单文件和多文件的区分,目前应用市场上的压缩包工具,解压缩操作也都仅支持一次处理一个压缩包文件。
zlib.compressFile 和 zlib.decompressFile,针对同步和异步场景,提供了不同实现,知其一便知其所有。
decompressFile 的参数结构,与 compressFile 几乎是一样的,而这也是众多三方 API 库共同具有的特征,所以,也是我们应当学会和掌握的编程细节。
4、获取文件信息
对于解压缩功能,往往在真正进行解压缩之前,会先读取压缩包的相关文件信息,比如获取
原始文件的大小,然后决定是否要执行解压缩操作,从而避免将用户的存储空间撑爆。
有需求便有市场,所以,zlib 模块提供了大量的用户获取压缩包文件信息的相关 API,有兴趣的到官方文档上仔细了解,这里重点介绍 zlib.getOriginalSize。
zlib.getOriginalSize 方法原型如下:
参数结构比较简单,仅需传入压缩包所在的沙箱路径即可,而压缩包对应的原始文件的大小,会以异步回调的方式返回,简单的使用如下:
et compressedFile = '/data/storage/el2/base/temp/test.zip';
try {
zlib.getOriginalSize(compressedFile).then((data: number) => {
console.info(`getOriginalSize success. getOriginalSize: ${data}`);
}).catch((errData: BusinessError) => {
console.error(`errData is errCode:${errData.code} message:${errData.message}`);
})
} catch (errData) {
let code = (errData as BusinessError).code;
let message = (errData as BusinessError).message;
console.error(`errData is errCode:${code} message:${message}`);
}
由于文件压缩与解压缩,在移动端应用场景中,并不多见,同时,为了避免本文篇幅过长,这里就不再给出文件压缩与解压缩的详细使用案例,请读者见谅。