以下内容均基于HarmonyOS NEXT版本
什么是AppStartup
应用启动时通常需要执行一系列初始化启动任务,如果将启动任务都放在应用主模块(即entry类型的Module)的UIAbility组件的onCreate生命周期中,那么只能在主线程中依次执行,不但影响应用的启动速度,而且当启动任务过多时,任务之间复杂的依赖关系还会使得代码难以维护。
HarmonyOS AppStartup是专为Stage模型设计的应用启动框架,通过声明式配置管理初始化任务,解决传统初始化方式的三大痛点:
- 启动速度瓶颈:支持异步线程(taskPool)执行任务,突破主线程串行限制
- 依赖管理混乱:采用DAG(有向无环图)管理任务依赖关系
- 代码维护困难:通过JSON配置中心化管理任务,提升可维护性
核心设计理念
-
分层架构:解耦启动任务与业务逻辑
-
线程优化:自动分配主线程/任务池线程
-
生命周期整合:与AbilityStage深度集成
-
可观测性:提供完整的生命周期监听接口
启动框架执行时机
约束限制
- 启动框架只支持在entry类型的Module中使用。
- 启动任务之间不允许存在循环依赖。
(可选)修改启动模式
- AppStartup分别提供了自动和手动两种方式来执行启动任务,默认采用自动模式。开发者可以根据需要修改为手动模式。
- 自动模式:当AbilityStage组件容器完成创建后,自动执行启动任务。
- 手动模式:在UIAbility完成创建后手动调用,来执行启动任务。对于某些使用频率不高的模块,不需要应用最开始启动时就进行初始化。开发者可以选择将该部分启动任务修改为手动模式,在应用启动完成后调用startupManager.run方法来执行启动任务。
- 例如下面例子中在EntryAbility的onCreate生命周期中介绍如何采用手动模式来启动任务
- 还可以在页面加载完成后,在页面中调用启动框架手动模式。
开发流程
一. 定义启动框架配置文件:
- 在应用主模块(即entry类型的Module)的"resources/base/profile"路径下,新建启动框架配置文件。文件名可以自定义,本文以"startup_config.json"为例。
- 在启动框架配置文件startup_config.json中,依次添加各个启动任务的配置信息。例如:在"ets/startup"路径下,依次创建6个启动任务文件、以及一个公共的启动参数配置文件。文件名称必须确保唯一性。6个启动任务依赖关系如下图所示,其中StartupTask_005,StartupTask_006为手动启动模式。
json
//startup_config.json 文件内容
{
//启动任务配置信息
"startupTasks": [
{
//启动任务名称,可自定义,推荐与类名保持一致。
"name": "StartupTask_001",
//启动任务对应的文件路径。
"srcEntry": "./ets/startup/StartupTask_001.ets",
//启动任务依赖的其他启动任务的类名数组。
"dependencies": [
"StartupTask_002",
"StartupTask_003"
],
//执行初始化所在的线程:
//mainThread:在主线程中执行,taskPool:在异步线程中执行,默认为mainThread
"runOnThread": "taskPool",
//主线程是否需要等待启动框架执行。
//当runOnThread取值为taskPool时,该字段生效。默认为true
"waitOnMainThread": false
},
{
"name": "StartupTask_002",
"srcEntry": "./ets/startup/StartupTask_002.ets",
"dependencies": [
"StartupTask_004"
],
"waitOnMainThread": false
},
{
"name": "StartupTask_003",
"srcEntry": "./ets/startup/StartupTask_003.ets",
"dependencies": [
"StartupTask_004"
],
"waitOnMainThread": false
},
{
"name": "StartupTask_004",
"srcEntry": "./ets/startup/StartupTask_004.ets",
"waitOnMainThread": false
},
{
"name": "StartupTask_005",
"srcEntry": "./ets/startup/StartupTask_005.ets",
"dependencies": [
"StartupTask_006"
],
"runOnThread": "mainThread",
"waitOnMainThread": true,
"excludeFromAutoStart": true //是否排除自动模式: true:手动模式,false:自动模式
},
{
"name": "StartupTask_006",
"srcEntry": "./ets/startup/StartupTask_006.ets",
"runOnThread": "mainThread",
"waitOnMainThread": false,
"excludeFromAutoStart": true
}
],
//启动参数配置文件所在路径
"configEntry": "./ets/startup/StartupConfig.ets"
}
二. 在module.json5配置文件的appStartup标签中,添加启动框架配置文件的索引。
json
{
"module": {
"name": "entry",
"type": "entry",
"appStartup": "$profile:startup_config", // 启动框架的配置文件
"srcEntry": "./ets/myabilitystage/MyAbilityStage.ets",
"mainElement": "EntryAbility",
// ...
}
}
三. 设置启动参数:
- 在启动参数配置文件(本文为"ets/startup/StartupConfig.ets"文件)中,使用StartupConfigEntry接口实现启动框架公共参数的配置,包括超时时间和启动任务的监听器等参数,其中需要用到如下接口:
- StartupConfig:用于设置任务超时时间和启动框架的监听器。
- StartupListener:用于监听启动任务是否执行成功。
typescript
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', `StartupConfig onConfig`);
let onCompletedCallback = (error: BusinessError<void>) => {
hilog.info(0x0000, 'testTag', `StartupConfig onCompletedCallback`);
if (error) {
hilog.info(0x0000, 'testTag', 'StartupConfig onCompletedCallback: %{public}d, message: %{public}s', error.code, error.message);
} else {
hilog.info(0x0000, 'testTag', `StartupConfig onCompletedCallback: success.`);
}
};
let startupListener: StartupListener = {
'onCompleted': onCompletedCallback
};
let config: StartupConfig = {
'timeoutMs': 10000,
'startupListener': startupListener
};
return config;
}
}
四. 为每个待初始化组件添加启动任务:
- 还需要在每个组件对应的启动任务文件中,通过实现StartupTask来添加启动任务。其中,需要用到下面的两个方法:
- init:启动任务初始化。当该任务依赖的启动任务全部执行完毕,即onDependencyCompleted完成调用后,才会执行init方法对该任务进行初始化。
- onDependencyCompleted:当前任务依赖的启动任务执行完成时,调用该方法。
typescript
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));
}
}
五. 手动启动模式
- 以UIAbility的onCreate生命周期中为例,介绍如何采用手动模式来启动任务
typescript
export default class EntryAbility extends UIAbility {
windowStage: window.WindowStage | undefined = undefined;
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'EntryAbility onCreate');
//AppStartup
let startParams = ['StartupTask_005', 'StartupTask_006'];
try {
hilog.info(0x0000, 'testTag', '%{public}s', 'EntryAbility startupManager.run');
startupManager.run(startParams).then(() => {
console.log('StartupTest startupManager run then, startParams = ');
}).catch((error: BusinessError) => {
console.info("StartupTest promise catch error, error = " + JSON.stringify(error));
console.info("StartupTest promise catch error, startParams = "
+ JSON.stringify(startParams));
})
} catch (error) {
let errMsg = JSON.stringify(error);
let errCode: number = error.code;
console.log('Startup catch error , errCode= ' + errCode);
console.log('Startup catch error ,error= ' + errMsg);
}
}
}
- 开发者还可以在页面加载完成后,在页面中调用启动框架手动模式,
typescript
import { startupManager } from '@kit.AbilityKit';
@Entry
@Component
struct Index {
@State message: string = '手动模式';
@State startParams: Array<string> = ["StartupTask_006"];
build() {
RelativeContainer() {
Button(this.message)
.id('AppStartup')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.onClick(() => {
if (!startupManager.isStartupTaskInitialized("StartupTask_006")) { // 判断是否已经完成初始化
startupManager.run(this.startParams)
}
})
.alignRules({
center: {anchor: '__container__', align: VerticalAlign.Center},
middle: {anchor: '__container__', align: HorizontalAlign.Center}
})
}
.height('100%')
.width('100%')
}
}
运行结果
- 启动app后查看log如下
less
I StartupConfig onConfig
I StartupTask_004 init.
I StartupTask_002 onDependencyCompleted, dependence: StartupTask_004, result: "StartupTask_004"
I StartupTask_003 onDependencyCompleted, dependence: StartupTask_004, result: "StartupTask_004"
I StartupTask_002 init.
I StartupTask_003 init.
I StartupTask_001 onDependencyCompleted, dependence: StartupTask_002, result: "StartupTask_002"
I StartupTask_001 onDependencyCompleted, dependence: StartupTask_003, result: "StartupTask_003"
I StartupTask_001 init.
I StartupConfig onCompletedCallback
I StartupConfig onCompletedCallback: success.
I EntryAbility onCreate
I EntryAbility startupManager.run
I StartupTask_006 init.
I StartupTask_005 onDependencyCompleted, dependence: StartupTask_006, result: "StartupTask_006"
I StartupTask_005 init.
I StartupConfig onCompletedCallback
I StartupConfig onCompletedCallback: success.
I EntryAbility onWindowStageCreate
I EntryAbility onForeground
I EntryAbility Succeeded in loading the content: pages/Index
适用场景对比
场景 | AppStartup优势 | 传统方式劣势 |
---|---|---|
多模块应用启动 | 统一依赖管理 | 分散初始化难以维护 |
跨设备协同 | 支持自适应线程策略 | 线程管理复杂 |
快速迭代项目 | 配置驱动开发 | 代码修改成本高 |
跨设备适配方案
typescript
// deviceType适配示例
export default class DeviceTask extends StartupTask {
async onExecute(): Promise<void> {
const deviceType = deviceInfo.deviceType;
switch(deviceType) {
case 'phone':
await this.loadPhoneSpecificData();
break;
case 'watch':
await this.loadWatchSpecificData();
break;
default:
this.skipTask();
}
this.markSuccess();
}
}
动态权限申请
scala
// 权限异步处理
export default class PermissionTask extends StartupTask {
async onExecute(): Promise<void> {
const granted = await requestPermissions([
{ name: 'ohos.permission.LOCATION' }
]);
if (!granted) {
this.skipTask();
}
}
}