合理使用系统提供的接口
简介
在应用开发中,经常会调用系统提供的接口,比如读取本地文件、处理服务端数据等等。若对接口使用不合理,可能引起延迟、卡顿、丢帧等性能问题。本文以如下系统提供的接口为例,总结了使用中的注意事项。
- ResourceManager的getXXXSync接口
- wordBreak属性
- IFAA免密认证获取匿名化ID
- 国际化使用Localization Kit接口替换ArkTS运行时接口
ResourceManager的getXXXSync接口
ResourceManager通过getXXXSync接口获取资源的方式有两种:通过resource对象resourceManager.getStringSync($r('app.string.test'))和通过idresourceManager.getStringSync($r('app.string.test').id)。
通过resource对象的方式在获取过程中发生了一次拷贝,通过id的方式只是对原对象的引用。
下面以getStringSync为例,测试一下这两种参数在方法中的使用是否会有耗时区别。
通过resource对象获取(源码示例链接)
@Entry
@Component
struct Index {
@State message: string = 'getStringSync';
aboutToAppear(): void {
hiTraceMeter.startTrace('getStringSync', 1);
// getStringSync接口的入参直接使用资源,未使用资源ID
getContext().resourceManager.getStringSync($r('app.string.test'));
hiTraceMeter.finishTrace('getStringSync', 1);
}
build() {
RelativeContainer() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.height('100%')
.width('100%')
}
}
通过SmartPerf Host工具抓取Trace。持续时间为1.621ms。

通过id获取(源码示例链接)
@Entry
@Component
struct Index {
@State message: string = 'getStringSyncAfter';
aboutToAppear(): void {
hiTraceMeter.startTrace('getStringSyncAfter', 2);
// getStringSync接口的入参使用了资源ID
getContext().resourceManager.getStringSync($r('app.string.test').id);
hiTraceMeter.finishTrace('getStringSyncAfter', 2);
}
build() {
RelativeContainer() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.height('100%')
.width('100%')
}
}
通过SmartPerf Host工具抓取Trace。持续时间为0.124ms。

总结
参数为资源信息时(1.621ms)比参数为资源ID值时(0.124ms)耗时更多。所以当需要使用类似方法时,使用资源ID值作为参数更优。
wordBreak属性
零宽空格(Zero Width Space, ZWSP)是一个特殊的Unicode字符。它是一个不可见的字符,其宽度为零,不占用任何可见空间。在文本处理系统中,尽管它在视觉上是不可见的,但它在文本中确实存在,并可以作为潜在的断点,即允许在此位置断开行。这意味着如果一行文本过长需要自动换行时,文本可以在零宽空格的位置进行折行,而不影响单词的完整性。
虽然零宽空格在许多情况下都是有用的,但它也可能引起问题,特别是在文本处理和数据清洗中。不注意这些看不见的字符可能导致数据的意外错误、搜索失败、数据不一致等问题。因此,在处理来自不同源的文本数据时,了解和考虑这些不可见字符是非常重要的。
避免在文本组件内使用零宽空格(\u200b)的形式来设置断行规则,推荐使用wordBreak,wordBreak在使用性能方面优于零宽空格。例如推荐用法为:Text(this.diskName).wordBreak(WordBreak.BREAK_ALL)。
反例(源码示例链接)
@CustomDialog
export struct DiskFormatDialog {
private diskName: string = '';
build() {
Column() {
Text(this.diskName.split("").join("\u200B"))
.textAlign(TextAlign.Start)
}
}
}
通过SmartPerf Host工具抓取Trace。启动时ReceiveVsync阶段耗时为3s271ms。
正例(源码示例链接)
@CustomDialog
export struct DiskFormatDialog {
private diskName: string = '';
build() {
Column() {
Text(this.diskName)
.textAlign(TextAlign.Start)
.wordBreak(WordBreak.BREAK_ALL)
}
}
}
通过SmartPerf Host工具抓取Trace。启动时ReceiveVsync阶段耗时为301ms。
总结
使用零宽空格时(3s271ms)比使用wordBreak时(301ms)耗时更多。所以当需要使用类似方法时,使用wordBreak性能更优。
IFAA免密认证获取匿名化ID
在应用开发中,开发者可以使用IFAA免密认证模块实现免密登录,免密支付等业务场景,其中在主线程中使用同步接口getAnonymousIdSync获取IFAA免密认证的匿名化ID时,容易阻塞主线程操作,带来性能问题。建议开发者使用异步接口getAnonymousId进行替换,异步获取IFAA免密认证的匿名化ID。
使用同步接口getAnonymousIdSync(源码示例链接)
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';
import { ifaa } from '@kit.OnlineAuthenticationKit';
// 开发者需要按照IIFAA的TLV格式构造入参,并转换为Uint8Array参数;此处arg需要开发者替换为真实入参。
let arg = new Uint8Array([0]);
@Entry
@Component
export struct GetAnonymousId {
@State message: string = 'getAnonymousId';
build() {
RelativeContainer() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(()=>{
hiTraceMeter.startTrace('getAnonymousId', 1);
ifaa.getAnonymousId(arg).then((getAnonIdResult: Uint8Array)=>{
})
hiTraceMeter.finishTrace('getAnonymousId', 1);
})
}
.height('100%')
.width('100%')
}
}
使用IDE的Profiler工具抓取Trace数据,查看同步接口getAnonymousIdSync的调用耗时为150.695ms。

使用异步接口getAnonymousId(源码示例链接)
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';
import { ifaa } from '@kit.OnlineAuthenticationKit';
// 开发者需要按照IIFAA的TLV格式构造入参,并转换为Uint8Array参数;此处arg需要开发者替换为真实入参。
let arg = new Uint8Array([0]);
@Entry
@Component
export struct GetAnonymousIdSync {
@State message: string = 'getAnonymousIdSync';
build() {
RelativeContainer() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(()=>{
hiTraceMeter.startTrace('getAnonymousIdSync', 1);
let getAnonIdResult: Uint8Array = ifaa.getAnonymousIdSync(arg);
hiTraceMeter.finishTrace('getAnonymousIdSync', 1);
})
}
.height('100%')
.width('100%')
}
}
使用IDE的Profiler工具抓取Trace数据,查看异步接口getAnonymousId的调用耗时为11.988ms。

总结
对比获取到的测试数据可以看出,异步接口getAnonymousId的调用耗时(11.988ms)远小于同步接口getAnonymousIdSync的调用耗时(150.695ms),因此建议开发者在主线程中获取IFAA免密认证的匿名化ID时,使用异步接口getAnonymousId获取。
国际化使用Localization Kit接口替换ArkTS运行时接口
在应用国际化处理时,由于ArkTS运行时兼容TS/JS,所以开发者可以直接使用国际化接口Intl实现包括时间日期格式化、数字格式化、排序格式化等国际化处理,而不需要导入额外模块。此外,开发者也可以使用Localization Kit接口intl实现相同功能。
但是,在分别使用Localization Kit接口intl和ArkTS运行时接口Intl创建同类对象时,Localization Kit接口的耗时更短,性能更优。并且在多次创建时,随着创建次数的增加,二者的耗时差异会更加明显。因此建议开发者在进行国际化开发时,使用Localization Kit模块提供的接口替换ArkTS运行时接口,减少执行耗时。
使用ArkTS运行时接口Intl(源码示例链接)
下面使用ArkTS运行时接口Intl执行100次对象创建,此时Intl不需要import导入,可以直接使用。
// 导入自定义打点模块
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';
// 循环创建100次RelativeTimeFormat对象,记录每次创建的耗时和总执行耗时
hiTraceMeter.startTrace('RelativeTimeFormat-100', 1);
for (let i = 0; i < 100; i++) {
hiTraceMeter.startTrace('RelativeTimeFormat-single', 2);
let relativeTimeFormat = new Intl.RelativeTimeFormat('zh-CN', { numeric: 'auto' });
hiTraceMeter.finishTrace('RelativeTimeFormat-single', 2);
}
hiTraceMeter.finishTrace('RelativeTimeFormat-100', 1);
// 同样的过程创建DateTimeFormat、NumberFormat等其他类的实例
使用Localization Kit接口intl(源码示例链接)
使用Localization Kit接口替换ArkTS运行时接口创建100次同类对象,使用前需要先从Localization Kit导入intl。示例代码如下:
// 导入自定义打点模块
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';
// 导入国际化接口intl
import { intl } from '@kit.LocalizationKit';
// 循环创建100次RelativeTimeFormat对象,记录每次创建的耗时和总执行耗时
hiTraceMeter.startTrace('RelativeTimeFormat-100', 1);
for (let i = 0; i < 100; i++) {
hiTraceMeter.startTrace('RelativeTimeFormat-single', 2);
let relativeTimeFormat = new intl.RelativeTimeFormat('zh-CN', { numeric: 'auto' });
hiTraceMeter.finishTrace('RelativeTimeFormat-single', 2);
}
hiTraceMeter.finishTrace('RelativeTimeFormat-100', 1);
// 同样的过程创建DateTimeFormat、NumberFormat等其他类的实例
性能对比
使用IDE的Profiler工具,分别获取使用ArkTS运行时接口和使用Localization Kit接口创建100次同类对象的耗时数据,从首次创建耗时、首次创建之后单次平均耗时和100次创建总耗时三个维度进行对比分析:
| 接口名 | 接口来源 | 首次创建耗时 | 首次创建之后单次平均耗时 | 100次创建总耗时 |
|---|---|---|---|---|
| RelativeTimeFormat | ArkTS运行时 | 29ms 960μs | 4ms 244.1μs | 452ms 826.6μs |
| Localization Kit | 18ms 323μs | 16μs | 20ms 285μs | |
| Locale | ArkTS运行时 | 55μs | 8μs | 1ms 65μs |
| Localization Kit | 26μs | 5μs | 744μs | |
| DateTimeFormat | ArkTS运行时 | 5ms 982μs | 719μs | 77ms 866μs |
| Localization Kit | 2ms 823μs | 250μs | 28ms 33μs | |
| NumberFormat | ArkTS运行时 | 5ms 579μs | 61μs | 11ms 950μs |
| Localization Kit | 200μs | 28μs | 3ms 209μs | |
| Collator | ArkTS运行时 | 13ms 141μs | 6ms 483.8μs | 656ms 828μs |
| Localization Kit | 657μs | 19μs | 2ms 823μs | |
| PluralRules | ArkTS运行时 | 355μs | 133μs | 13ms 901μs |
| Localization Kit | 254μs | 21μs | 2ms 609μs |
说明: 以上数据来源均为版本DevEco Studio 5.0.3.706、HarmonyOS NEXT Developer Beta6 SDK条件下测试得到,不同设备类型数据可能存在差异,测试数据旨在体现性能优化趋势,仅供参考。
从测试数据可以看出,无论是在首次创建还是在后续创建过程中,使用Localization Kit接口创建同类对象的耗时均少于使用ArkTS运行时接口,并且随着创建次数增加,总耗时差异也会逐渐增大,其中RelativeTimeFormat和Collator的收益更为明显。
因此在应用国际化时,建议开发者选择使用Localization Kit接口替换ArkTS运行时接口,提升应用性能。