应用启动时通常需要执行一系列初始化启动任务,如果将启动任务都放在HAP的UIAbility组件的onCreate生命周期中,那么只能在主线程中依次执行,不但影响应用的启动速度,而且当启动任务过多时,任务之间复杂的依赖关系还会使得代码难以维护。
AppStartup提供了一种简单高效的应用启动方式,可以支持任务的异步启动,加快应用启动速度。同时,通过在一个配置文件中统一设置多个启动任务的执行顺序以及依赖关系,让执行启动任务的代码变得更加简洁清晰、容易维护。
启动框架支持以自动模式或手动模式执行启动任务,默认采用自动模式。在构造AbilityStage组件容器过程中开始加载开发者配置的启动任务,并执行自动模式的启动任务。开发者也可以在AbilityStage创建完后调用startupManager.run方法,执行手动模式的启动任务。
图1 启动框架执行时机
HAP:entry类型的HAP支持以自动和手动模式启动。从API version 20开始,feature类型的HAP支持以自动和手动模式启动。
HSP/HAR: 从API version 18开始,支持在HSP和HAR中配置启动任务。HSP和HAR的启动任务、so预加载任务无法主动配置为自动模式,但可以被HAP中自动模式的启动任务、so预加载任务拉起。
启动框架从API 18开始支持配置so预加载任务,so文件开发可以参考Node-API创建Native C++工程。
使用启动框架必须在HAP的module.json5配置文件中开启启动框架。
ExtensionAbility组件启动场景单一,使用启动框架会带来额外开销,因此不支持ExtensionAbility组件启动时拉起启动框架。
启动任务之间或so预加载任务之间不允许存在循环依赖。
在HAP的“resources/base/profile”路径下,新建启动框架配置文件。文件名可以自定义,本文以"startup_config.json"为例。
在启动框架配置文件startup_config.json中,依次添加各个启动任务和so预加载任务的配置信息。
假设当前应用启动框架共包含6个启动任务和6个so预加载任务,任务之间的依赖关系如下图所示。为了便于并发执行启动任务,单个启动任务文件包含的启动任务应尽量单一,本例中每个启动任务对应一个启动任务文件。不建议应用在so文件的加载回调中运行代码逻辑,so文件的加载不宜过长,否则会影响主线程的运行。
图2 启动任务与so预加载任务依赖关系图
在“ets/startup”路径下,依次创建6个启动任务文件、以及一个公共的启动参数配置文件。文件名称必须确保唯一性。
在启动框架配置文件startup_config.json中,添加所有启动任务以及启动参数配置文件的相关信息。
startup_config.json文件示例如下:
{
"startupTasks": [
{
"name": "StartupTask_001",
"srcEntry": "./ets/startup/StartupTask_001.ets",
"dependencies": [
"StartupTask_002",
"StartupTask_003"
],
"runOnThread": "taskPool",
"waitOnMainThread": false
},
{
"name": "StartupTask_002",
"srcEntry": "./ets/startup/StartupTask_002.ets",
"dependencies": [
"StartupTask_003",
"StartupTask_004"
],
"runOnThread": "taskPool",
"waitOnMainThread": false
},
{
"name": "StartupTask_003",
"srcEntry": "./ets/startup/StartupTask_003.ets",
"dependencies": [
"StartupTask_004"
],
"runOnThread": "taskPool",
"waitOnMainThread": false
},
{
"name": "StartupTask_004",
"srcEntry": "./ets/startup/StartupTask_004.ets",
"runOnThread": "taskPool",
"waitOnMainThread": false
},
{
"name": "StartupTask_005",
"srcEntry": "./ets/startup/StartupTask_005.ets",
"dependencies": [
"StartupTask_006"
],
"runOnThread": "mainThread",
"waitOnMainThread": true,
"excludeFromAutoStart": true
},
{
"name": "StartupTask_006",
"srcEntry": "./ets/startup/StartupTask_006.ets",
"runOnThread": "mainThread",
"waitOnMainThread": false,
"excludeFromAutoStart": true
}
],
"appPreloadHintStartupTasks": [
{
"name": "libentry_001",
"srcEntry": "libentry_001.so",
"dependencies": [
"libentry_002",
"libentry_003"
],
"runOnThread": "taskPool"
},
{
"name": "libentry_002",
"srcEntry": "libentry_002.so",
"dependencies": [
"libentry_003",
"libentry_004"
],
"runOnThread": "taskPool"
},
{
"name": "libentry_003",
"srcEntry": "libentry_003.so",
"dependencies": [
"libentry_004"
],
"runOnThread": "taskPool"
},
{
"name": "libentry_004",
"srcEntry": "libentry_004.so",
"runOnThread": "taskPool"
},
{
"name": "libentry_005",
"srcEntry": "libentry_005.so",
"dependencies": [
"libentry_006"
],
"runOnThread": "taskPool",
"excludeFromAutoStart": true
},
{
"name": "libentry_006",
"srcEntry": "libentry_006.so",
"runOnThread": "taskPool",
"excludeFromAutoStart": true
}
],
"configEntry": "./ets/startup/StartupConfig.ets"
}
表1 startup_config.json配置文件标签说明
属性名称 | 含义 | 数据类型 | 是否可缺省 |
---|---|---|---|
startupTasks | 启动任务配置信息,标签说明详见下表。 | 对象数组 | 该标签可缺省,缺省值为空。 |
appPreloadHintStartupTasks | 预加载so任务配置信息,标签说明详见下表。 | 对象数组 | 该标签可缺省,缺省值为空。 |
configEntry | 启动参数配置文件所在路径。 说明: HSP、HAR中不允许配置 configEntry 字段。 |
字符串 | 该标签不可缺省。 |
表2 startupTasks标签说明
属性名称 | 含义 | 数据类型 | 是否可缺省 |
---|---|---|---|
name | 启动任务名称,可自定义,推荐与类名保持一致。 | 字符串 | 该标签不可缺省。 |
srcEntry | 启动任务对应的文件路径。 | 字符串 | 该标签不可缺省。 |
dependencies | 启动任务依赖的其他启动任务的类名数组。 | 对象数组 | 该标签可缺省,缺省值为空。 |
excludeFromAutoStart | 是否排除自动模式,详细介绍可以查看修改启动模式。 - true:手动模式。 - false:自动模式。 说明: HSP、HAR中startupTask里的excludeFromAutoStart标签必须配置为true。 |
布尔值 | 该标签可缺省,缺省值为false。 |
runOnThread | 执行初始化所在的线程。 - mainThread :在主线程中执行。- taskPool :在异步线程中执行。 |
字符串 | 该标签可缺省,缺省值为mainThread 。 |
waitOnMainThread | 主线程是否需要等待启动框架执行。当runOnThread取值为taskPool 时,该字段生效。 - true:主线程等待启动框架执行完之后,才会加载应用首页。 - false:主线程不等待启动任务执行。 |
布尔值 | 该标签可缺省,缺省值为true。 |
matchRules | 该字段用于筛选需要以自动模式启动的启动任务,加速应用启动过程。适用于快速拉起某个页面的场景,例如,通过桌面卡片、通知或意图调用等方式触发的页面跳转,实现功能服务的一步直达体验。操作指导详见添加任务匹配规则。 说明: - 从API version 20开始,支持该字段。当前仅支持在HAP中配置该字段。 - 该字段的优先级高于excludeFromAutoStart。如果所有启动任务均匹配失败,则按任务的excludeFromAutoStart配置处理。 |
对象 | 该标签可缺省。 |
表3 appPreloadHintStartupTasks标签说明
属性名称 | 含义 | 数据类型 | 是否可缺省 |
---|---|---|---|
name | 预加载so文件名。 | 字符串 | 该标签不可缺省。 |
srcEntry | 带后缀预加载so文件名。 | 字符串 | 该标签不可缺省。 |
dependencies | 预加载任务依赖的其他预加载任务的so名数组。 | 对象数组 | 该标签可缺省,缺省值为空。 |
excludeFromAutoStart | 是否排除自动模式,详细介绍可以查看修改启动模式。 - true:手动模式。 - false:自动模式。 说明: HSP、HAR中appPreloadHintStartupTask的excludeFromAutoStart标签必须配置为true。 |
布尔值 | 该标签可缺省,缺省值为false。 |
runOnThread | 执行预加载所在的线程。 - taskPool :在异步线程中执行。说明: so预加载只允许在 taskPool 线程执行。 |
字符串 | 该标签不可缺省。 |
matchRules | 该字段用于筛选需要以自动模式启动的预加载so任务,加速应用启动过程。适用于快速拉起某个页面的场景,例如,通过桌面卡片、通知或意图调用等方式触发的页面跳转,实现功能服务的一步直达体验。操作指导详见添加任务匹配规则。 说明: - 从API version 20开始,支持该字段。当前仅支持在HAP中配置该字段。 - 该字段的优先级高于excludeFromAutoStart。如果所有预加载so任务均匹配失败,则按任务的excludeFromAutoStart配置处理。 |
对象 | 该标签可缺省。 |
在module.json5配置文件的appStartup标签中,添加启动框架配置文件的索引。
module.json5示例代码如下。
{
"module": {
"name": "entry",
"type": "entry",
// ...
"appStartup": "$profile:startup_config", // 启动框架的配置文件
// ...
}
}
在启动参数配置文件(本文为“ets/startup/StartupConfig.ets”文件)中,使用StartupConfigEntry接口实现启动框架公共参数的配置,包括超时时间和启动任务的监听器等参数,其中需要用到如下接口:
import { StartupConfig, StartupConfigEntry, StartupListener } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
export default class MyStartupConfigEntry extends StartupConfigEntry {
onConfig() {
hilog.info(0x0000, 'testTag', `onConfig`);
let onCompletedCallback = (error: BusinessError<void>) => {
hilog.info(0x0000, 'testTag', `onCompletedCallback`);
if (error) {
hilog.error(0x0000, 'testTag', 'onCompletedCallback: %{public}d, message: %{public}s', error.code, error.message);
} else {
hilog.info(0x0000, 'testTag', `onCompletedCallback: success.`);
}
};
let startupListener: StartupListener = {
'onCompleted': onCompletedCallback
};
let config: StartupConfig = {
'timeoutMs': 10000,
'startupListener': startupListener
};
return config;
}
}
上述操作中已完成启动框架配置文件、启动参数的配置,还需要在每个功能组件对应的启动任务文件中,通过实现StartupTask来添加启动任务。其中,需要用到下面的两个方法:
下面以startup_config.json中的StartupTask_001.ets文件为例,示例代码如下。开发者需要分别为每个待初始化功能组件添加启动任务。
说明:
由于StartupTask采用了Sendable协议,在继承该接口时,必须添加Sendable注解。
import { StartupTask, common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
@Sendable
export default class StartupTask_001 extends StartupTask {
constructor() {
super();
}
async init(context: common.AbilityStageContext) {
hilog.info(0x0000, 'testTag', 'StartupTask_001 init.');
return 'StartupTask_001';
}
onDependencyCompleted(dependence: string, result: Object): void {
hilog.info(0x0000, 'testTag', 'StartupTask_001 onDependencyCompleted, dependence: %{public}s, result: %{public}s',
dependence, JSON.stringify(result));
}
}
通常大型应用会有多个HSP和HAR,本节将提供一个应用示例,以展示如何在HSP包和HAR包中使用启动框架。该示例应用包括两个HSP包(hsp1、hsp2)和一个HAR包(har1),并且包含启动任务和so预加载任务。
开发步骤如下:
除HAP外,在HSP包和HAR包的“resources/base/profile”目录下创建启动框架配置文件,不同模块可以使用相同文件名,本文以"startup_config.json"为例。
分别在各个模块的启动框架配置文件startup_config.json中, 添加对应的配置信息。
假设当前应用存在的启动任务与so预加载任务如下表所示。
表4 应用启动任务与so预加载任务说明
模块 | 启动任务 | so预加载任务 |
---|---|---|
entry | HAP_Task_01 | libentry_01 |
hsp1 | HSP1_Task_01 HSP1_Task_02 |
libhsp1_01 libhsp1_02 |
hsp2 | HSP2_Task_01 | libhsp2_01 |
har | HAR1_Task_01 | libhar1_01 |
图3 启动任务与so预加载依赖关系图
HAP的startup_config.json可参考定义启动框架配置文件,HSP与HAR的startup_config.json文件无法配置"configEntry"字段,以hsp1包配置文件为例,示例如下:
{
"startupTasks": [
{
"name": "HSP1_Task_01",
"srcEntry": "./ets/startup/HSP1_Task_01.ets",
"dependencies": [
"HSP1_Task_02",
"HAR1_Task_01"
],
"runOnThread": "taskPool",
"waitOnMainThread": false,
"excludeFromAutoStart": true
}
],
"appPreloadHintStartupTasks": [
{
"name": "libhsp1_01",
"srcEntry": "libhsp1_01.so",
"dependencies": [
"libhsp1_02",
"libhar1_01"
],
"runOnThread": "taskPool",
"excludeFromAutoStart": true
}
]
}
分别在各个模块的module.json5配置文件的appStartup标签中,添加启动框架配置文件的索引。
hsp1、hsp2以及har1的module.json5示例代码如下。
{
"module": {
"name": "hsp1",
"type": "shared",
// ...
"appStartup": "$profile:startup_config", // 启动框架的配置文件
// ...
}
}
{
"module": {
"name": "hsp2",
"type": "shared",
// ...
"appStartup": "$profile:startup_config", // 启动框架的配置文件
// ...
}
}
{
"module": {
"name": "har1",
"type": "har",
// ...
"appStartup": "$profile:startup_config", // 启动框架的配置文件
// ...
}
}
其余步骤请参考设置启动参数和为每个待初始化功能组件添加启动任务章节进行配置。
AppStartup分别提供了自动和手动两种方式来执行启动任务,entry模块中默认采用自动模式,开发者可以根据需要修改为手动模式,HSP与HAR只能配置为手动模式。
下面以UIAbility的onCreate生命周期中为例,介绍如何采用手动模式来启动任务,示例代码如下。
import { AbilityConstant, UIAbility, Want, startupManager } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
let startParams = ['StartupTask_005', 'StartupTask_006'];
try {
startupManager.run(startParams).then(() => {
console.log(`StartupTest startupManager run then, startParams = ${JSON.stringify(startParams)}.`);
}).catch((error: BusinessError) => {
console.error(`StartupTest promise catch error, error = ${JSON.stringify(error)}.`);
console.error(`StartupTest promise catch error, startParams = ${JSON.stringify(startParams)}.`);
})
} catch (error) {
let errMsg = (error as BusinessError).message;
let errCode = (error as BusinessError).code;
console.error(`Startup catch error, errCode= ${errCode}.`);
console.error(`Startup catch error, errMsg= ${errMsg}.`);
}
}
// ...
}
开发者还可以在页面加载完成后,在页面中调用启动框架手动模式,示例代码如下。
import { startupManager } from '@kit.AbilityKit';
@Entry
@Component
struct Index {
@State message: string = "手动模式";
@State startParams1: Array<string> = ["StartupTask_006"];
@State startParams2: Array<string> = ["libentry_006"];
build() {
RelativeContainer() {
Button(this.message)
.id('AppStartup')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.onClick(() => {
if (!startupManager.isStartupTaskInitialized("StartupTask_006") ) { // 判断是否已经完成初始化
startupManager.run(this.startParams1)
}
if (!startupManager.isStartupTaskInitialized("libentry_006") ) {
startupManager.run(this.startParams2)
}
})
.alignRules({
center: {anchor: '__container__', align: VerticalAlign.Center},
middle: {anchor: '__container__', align: HorizontalAlign.Center}
})
}
.height('100%')
.width('100%')
}
}
在通过卡片、通知、意图调用等方式拉起某个页面时,为了实现功能服务一步直达,可以通过添加matchRules匹配规则,仅加载与当前场景相关的部分启动任务,无需加载全部默认的自动启动任务,以提高启动性能。
可以通过以下两种方式添加匹配规则:
通过matchRules中的uris、actions、insightIntents字段,根据UIAbility启动时的uri、action或意图名称,匹配不同场景启动任务及预加载so任务。
如果上述方式不能满足需求,可以通过matchRules中的customization自定义匹配规则。
表 matchRules标签说明
属性名称 | 含义 | 数据类型 | 是否可缺省 | 适用场景 |
---|---|---|---|---|
uris | 表示自动模式执行的任务的uri取值范围。当UIAbility启动时,会将Want中携带的uri属性,与此处配置的uris数组取值进行匹配。格式为scheme://host/path ,uri中的其它内容会被忽略(如port、fragment等)。 |
字符串数组 | 可缺省,缺省值为空。 | 通过特定uri拉起UIAbility的场景。 |
actions | 表示自动模式执行的任务的action取值范围。当UIAbility启动时,会将Want中携带的action属性,与此处配置的actions数组取值进行匹配。 | 字符串数组 | 可缺省,缺省值为空。 | 通过特定action拉起UIAbility的场景。 |
insightIntents | 表示自动模式执行的任务的意图名称取值范围。当UIAbility启动时,会将意图名称与此处配置的insightIntents数组取值进行匹配。 | 字符串数组 | 可缺省,缺省值为空。 | 通过特定意图名称拉起UIAbility的场景。 |
customization | 表示自动模式执行的任务的自定义规则取值范围。通过实现StartupConfigEntry的onRequestCustomMatchRule接口返回自定义规则值。当UIAbility启动时,会将自定义规则值与此处配置的customization数组取值进行匹配。 说明: 仅支持startupTasks中的任务配置。 |
字符串数组 | 可缺省,缺省值为空。 | 如果使用uris、actions、insightIntents字段无法满足要求,可以使用customization自定义规则。 |
说明:
- uris、insightIntents、actions、customization任一属性匹配成功即为任务匹配成功。
- 匹配成功的任务及其依赖任务都将在自动模式执行。
- 所有任务均匹配失败,则按任务的excludeFromAutoStart配置处理。
下面以uri匹配(action和意图名称类似)和customization匹配来举例,介绍如何实现添加任务匹配规则来筛选启动任务。
场景1:uri匹配
假定需要用户点击通知消息跳转到通知详情页面时,仅自动执行StartupTask_004和libentry_006任务。若启动通知详情UIAbility时Want中的uri属性为test://com.example.startupdemo/notification
,可以通过uri匹配。示例如下:
对定义启动框架配置文件步骤中的startup_config.json文件进行修改,增加StartupTask_004任务和libentry_006任务的matchRules配置。
{
"startupTasks": [
{
"name": "StartupTask_004",
"srcEntry": "./ets/startup/StartupTask_004.ets",
"runOnThread": "taskPool",
"waitOnMainThread": false,
"matchRules": {
"uris": [
"test://com.example.startupdemo/notification"
]
}
},
],
"appPreloadHintStartupTasks": [
{
"name": "libentry_006",
"srcEntry": "libentry_006.so",
"runOnThread": "taskPool",
"excludeFromAutoStart": true,
"matchRules": {
"uris": [
"test://com.example.startupdemo/notification"
]
}
}
],
"configEntry": "./ets/startup/StartupConfig.ets"
}
场景2:customization匹配
假定需要用户点击天气卡片跳转到天气界面时,仅自动执行StartupTask_006启动任务和excludeFromAutoStart=false配置的预加载so任务。若启动天气UIAbility时Want中传入的自定义参数fromType
为card
,可以通过customization匹配。示例如下:
对设置启动参数步骤中的MyStartupConfigEntry.ets文件进行修改,新增onRequestCustomMatchRule方法。
import { StartupConfig, StartupConfigEntry, StartupListener, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
export default class MyStartupConfigEntry extends StartupConfigEntry {
// onConfig ...
onRequestCustomMatchRule(want: Want): string {
if (want?.parameters?.fromType == 'card') {
return 'ruleCard';
}
return "";
}
}
对定义启动框架配置文件步骤中的startup_config.json文件进行修改,增加StartupTask_006任务的matchRules配置。预加载so任务不支持customization字段,按任务原有的excludeFromAutoStart配置处理。
{
"startupTasks": [
{
"name": "StartupTask_006",
"srcEntry": "./ets/startup/StartupTask_006.ets",
"runOnThread": "mainThread",
"waitOnMainThread": false,
"excludeFromAutoStart": true,
"matchRules": {
"customization": [
"ruleCard"
]
}
}
],
"configEntry": "./ets/startup/StartupConfig.ets"
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。