鸿蒙开发笔记-15-应用启动框架AppStartup

以下内容均基于HarmonyOS NEXT版本

什么是AppStartup

应用启动时通常需要执行一系列初始化启动任务,如果将启动任务都放在应用主模块(即entry类型的Module)的UIAbility组件的onCreate生命周期中,那么只能在主线程中依次执行,不但影响应用的启动速度,而且当启动任务过多时,任务之间复杂的依赖关系还会使得代码难以维护。

HarmonyOS AppStartup是专为Stage模型设计的应用启动框架,通过声明式配置管理初始化任务,解决传统初始化方式的三大痛点:

  • 启动速度瓶颈:支持异步线程(taskPool)执行任务,突破主线程串行限制
  • 依赖管理混乱:采用DAG(有向无环图)管理任务依赖关系
  • 代码维护困难:通过JSON配置中心化管理任务,提升可维护性

核心设计理念

  • 分层架构:解耦启动任务与业务逻辑

  • 线程优化:自动分配主线程/任务池线程

  • 生命周期整合:与AbilityStage深度集成

  • 可观测性:提供完整的生命周期监听接口

启动框架执行时机

约束限制

  1. 启动框架只支持在entry类型的Module中使用。
  2. 启动任务之间不允许存在循环依赖。

(可选)修改启动模式

  • AppStartup分别提供了自动和手动两种方式来执行启动任务,默认采用自动模式。开发者可以根据需要修改为手动模式。
  1. 自动模式:当AbilityStage组件容器完成创建后,自动执行启动任务。
  2. 手动模式:在UIAbility完成创建后手动调用,来执行启动任务。对于某些使用频率不高的模块,不需要应用最开始启动时就进行初始化。开发者可以选择将该部分启动任务修改为手动模式,在应用启动完成后调用startupManager.run方法来执行启动任务。
  • 例如下面例子中在EntryAbility的onCreate生命周期中介绍如何采用手动模式来启动任务
  • 还可以在页面加载完成后,在页面中调用启动框架手动模式。

开发流程

一. 定义启动框架配置文件:
  1. 在应用主模块(即entry类型的Module)的"resources/base/profile"路径下,新建启动框架配置文件。文件名可以自定义,本文以"startup_config.json"为例。
  2. 在启动框架配置文件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接口实现启动框架公共参数的配置,包括超时时间和启动任务的监听器等参数,其中需要用到如下接口:
  1. StartupConfig:用于设置任务超时时间和启动框架的监听器。
  2. 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来添加启动任务。其中,需要用到下面的两个方法:
  1. init:启动任务初始化。当该任务依赖的启动任务全部执行完毕,即onDependencyCompleted完成调用后,才会执行init方法对该任务进行初始化。
  2. 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();
    }
  }
}

我是今阳,如果想要进阶和了解更多的干货,欢迎关注微信公众号 "今阳说" 接收我的最新文章

相关推荐
桃子酱紫君5 小时前
华为配置篇-ISIS基础实验
华为
一笑的小酒馆6 小时前
Android在ksp中简单使用Room
android
泡泡大魔王6 小时前
鸿蒙ArkTS开发:微信/系统来电通话监听功能实现
华为·harmonyos
黑臂麒麟6 小时前
harmonyOS基础- 快速弄懂HarmonyOS ArkTs基础组件、布局容器(前端视角篇)
harmonyos·arkui
Yeats_Liao6 小时前
华为开源自研AI框架昇思MindSpore应用案例:基于MindSpore框架实现PWCNet光流估计
人工智能·华为
meimeiqian7 小时前
flutter android端抓包工具
android·flutter
Android技术之家7 小时前
谷歌决定终止开源Android以及对开发者的影响
android·开源
The旺7 小时前
《HarmonyOS Next开发进阶:打造功能完备的Todo应用华章》
harmonyos
每次的天空8 小时前
Android Jetpack学习总结(源码级理解)
android·学习·android jetpack
木子庆五9 小时前
Android设计模式之代理模式
android·设计模式·代理模式