一、前言

在 HarmonyOS(鸿蒙)的 Stage 应用模型中,UIAbility 是承载用户交互的核心组件。

  • 一个 Module 被加载时,谁来统一管理其中的 UIAbility?
  • 重复启动 UIAbility 时,系统是「新建实例」还是「复用实例」?由谁决定?
  • 不同 UIAbility 之间如何传递数据、如何拉起对方?

二、基本概念

1. UIAbility 组件

一种包含 UI 的应用组件,主要用于和用户交互。新建工程时,IDE 默认生成一个持有 UIAbility 的 Module 作为应用主模块,系统默认的 EntryAbility 类即继承自 UIAbility

2. ExtensionAbility 组件

不带 UI 的扩展能力组件,由相应的系统服务统一管理。常见类型有:

  • InputMethodExtensionAbility:输入法场景
  • WorkSchedulerExtensionAbility:闲时任务场景

3. HAP

一个 APP 可以包含一个或多个 HAP。包含 UIAbility 或 ExtensionAbility 的 Module 可以单独运行,编译后会生成一个 .hap 文件。当 HAP 中的代码首次被加载到进程中(即 Module 初始化)时,系统会首先创建一个 AbilityStage 实例。

4. AbilityStage

AbilityStageModule 级别 的组件容器,用于管理该 Module 中的 UIAbility 和 ExtensionAbility。它与编译期的 HAP 一一对应。

5. 运行期与编译期关系

维度 编译期 运行期
应用 App Application
模块 HAP AbilityStage
组件 UIAbility / ExtensionAbility UIAbility 实例 / ExtensionAbility 实例

运行期为 ApplicationAbilityStageUIAbilityExtensionAbility 分别提供对应的上下文环境:ApplicationContextAbilityStageContextUIAbilityContextExtensionAbilityContext,开发者可通过上下文调用各种资源和能力。


三、UIAbility 与 WindowStage 的生命周期联动

UIAbility 组件和 WindowStage 各自拥有一套生命周期。在设备上首次启动某个 UIAbility 时,三者的执行顺序如下:

  1. 系统首先创建持有该 UIAbility 的 AbilityStage 实例,创建成功后执行其 onCreate 回调(可做 Module 级资源初始化)。
  2. 系统为该 UIAbility 创建实例,创建成功后执行其 onCreate 回调(变量定义、资源加载等)。
  3. 系统为 UIAbility 实例创建一个 WindowStage 实例(一一对应),创建成功后执行 onWindowStageCreate 回调(加载 UI、订阅窗口事件)。
  4. UIAbility 进入前台,执行 onForeground
  5. 销毁前先执行 onWindowStageDestroy(释放 UI 资源),再执行 onDestroy

这种松耦合设计的好处

  1. 业务逻辑与 UI 逻辑分离:UIAbility 处理与页面无关的业务(蓝牙、数据库),WindowStage 上的 ArkUI 处理界面逻辑。
  2. 便于系统裁剪:无屏设备运行应用时不会创建窗口模块,减少 ROM 占用。
  3. 多设备复用同一套生命周期:系统自动判断设备形态并执行对应的窗口生命周期流程。

单窗口 vs 多窗口的任务切换

  • 单窗口(移动设备):任务一切换到任务二 → 任务一触发 WindowStage 的 InActive/Hidden,继而 UIAbility 进入 Background;任务二触发 Shown/Active,继而 UIAbility 进入 Foreground
  • 多窗口(2in1 设备):窗口可见性变化更细粒度,但 UIAbility 生命周期联动逻辑一致。

四、AbilityStage 组件容器详解

1. AbilityStage 的核心能力

AbilityStage 最主要的能力有两点:

  • 初始化模块(资源预加载、线程创建等);
  • 对以「指定实例模式」启动的 UIAbility 进行匹配处理

系统提供 4 个回调函数:

回调函数 触发时机 典型用途
onCreate AbilityStage 实例创建完成 模块初始化、资源预加载、线程创建
onAcceptWant specified 模式 UIAbility 启动时 返回实例唯一 key,供系统判断实例是否已存在
onConfigurationUpdated 系统全局配置变化 获取最新深浅色模式等,做主题定制
onMemoryLevel 系统调整内存时 释放不必要资源,避免进程被停止

2. AbilityStage 的创建与使用

DevEco Studio 默认工程中不会自动生成 AbilityStage,需要手动创建。

步骤 1:新建目录

在 Module 的 ets 目录下右键 → New → Directory,命名为 myabilitystage(或 application)。

步骤 2:新建 ArkTS 文件

在新建目录下右键 → New → ArkTS File,命名为 MyAbilityStage.ets

步骤 3:自定义类继承 AbilityStage
import { AbilityStage, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG = '[Sample_AbilityStage]';
const DOMAIN = 0x0000;

export default class MyAbilityStage extends AbilityStage {
  // 模块加载时触发
  onCreate(): void {
    hilog.info(DOMAIN, TAG, 'MyAbilityStage 初始化成功');
  }

  // specified 模式核心:返回实例唯一标识
  onAcceptWant(want: Want): string {
    if (want && want.abilityName === 'SpecifiedAbility') {
      const instanceKey = want.parameters?.instanceKey;
      if (instanceKey) {
        return `SpecifiedAbility_${instanceKey}`;
      }
    }
    return '';
  }

  // 系统配置变化(如深浅色模式)
  onConfigurationUpdated(config: Configuration): void {
    hilog.info(DOMAIN, TAG, `配置更新,colorMode: ${config.colorMode}`);
  }

  // 系统内存告警
  onMemoryLevel(level: AbilityConstant.MemoryLevel): void {
    hilog.info(DOMAIN, TAG, `内存等级变化: ${level}`);
  }
}
步骤 4:配置 HAP 加载入口

module.json5 中将 srcEntry 字段指向 AbilityStage 文件路径:

{
  "module": {
    "name": "entry",
    "type": "entry",
    "srcEntry": "./ets/application/MyAbilityStage.ets"
  }
}

配置完成后,启动 UIAbility 时系统会根据 srcEntry 找到 AbilityStage 文件并创建对应实例。


五、UIAbility 的三种启动模式

启动模式决定了 UIAbility 实例在启动时的呈现状态。针对不同业务场景,系统提供三种模式:

1. singleton(单实例模式)—— 默认

  • 定义:该类型 UIAbility 全局仅存在一个实例,无论启动多少次都复用已有实例。
  • 配置module.json5launchType: "singleton"(默认值,可省略)。
  • 适用场景:应用主界面、个人中心、购物车等需要全局唯一状态的页面。
  • 生命周期:首次启动走 onCreateonWindowStageCreateonForeground;后续启动仅触发 onNewWantonForeground

2. multiton(多实例模式)

  • 定义:每次启动都创建一个新实例,实例之间相互独立。
  • 配置launchType: "multiton"(旧名 standard,功能一致)。
  • 适用场景:浏览器多标签页、多窗口文档编辑、多任务处理。
  • 生命周期:每次启动都完整走 onCreateonWindowStageCreateonForeground,任务列表中可见多个任务。

3. specified(指定实例模式)

  • 定义:系统根据唯一标识(key)判断复用逻辑——相同标识复用实例,不同标识创建新实例。
  • 配置launchType: "specified"
  • 依赖必须在 AbilityStage 中实现 onAcceptWant,由它返回实例标识。
  • 适用场景:文档应用(新建文档 vs 打开已保存文档)、聊天窗口(按联系人 ID 复用)、商品详情页(按商品 ID 复用)。

三种模式对比

特性 singleton multiton specified
实例数量 全局唯一 无限制,每次新建 按标识分组,每组一个
复用规则 始终复用已有实例 永不复用,每次新建 相同标识复用,不同标识新建
核心生命周期 首次 onCreate,后续 onNewWant 每次启动均 onCreate 新标识 onCreate,同标识 onNewWant
依赖 AbilityStage (必须自定义实现)
关键参数 instanceKey(业务唯一标识)
适用场景 主界面、个人中心、购物车 多标签页、多文档编辑 聊天窗口、商品详情页

六、specified 模式实战:模拟文档应用

以文档类应用为例:

  • 点击「新建文档」→ 新建 UIAbility 实例;
  • 再次点击「新建文档」→ 再新建一个实例;
  • 点击「打开已保存文档」→ 拉起对应 key 的实例(若已存在则复用)。

工程结构

entry/src/main/ets/
├── application/
│   └── MyAbilityStage.ets        # 自定义 AbilityStage
├── entryability/
│   └── EntryAbility.ets          # 调用方(singleton,加载主界面)
├── specifiedability/
│   └── SpecifiedAbility.ets      # 被调用方(specified 模式)
├── pages/
│   ├── Index.ets
│   ├── Home.ets                  # 测试首页
│   └── SpecifiedPage.ets         # 文档页面

第一步:配置启动模式

module.json5 中将 SpecifiedAbilitylaunchType 设置为 specified,并配置 srcEntry 指向 AbilityStage:

{
  "module": {
    "name": "entry",
    "type": "entry",
    "srcEntry": "./ets/application/MyAbilityStage.ets",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "launchType": "singleton"
      },
      {
        "name": "SpecifiedAbility",
        "srcEntry": "./ets/specifiedability/SpecifiedAbility.ets",
        "launchType": "specified"
      }
    ]
  }
}

建议为 specified 模式的 UIAbility 设置 removeMissionAfterTerminate: true,使 UIAbility 生命周期结束即从任务列表移除,避免任务列表出现重复任务。

第二步:调用方启动逻辑

import { common, Want } from '@kit.AbilityKit';
import { getContext } from '@kit.ArkUI';
import { BusinessError } from '@ohos.base';

@Entry
@Component
struct Home {
  private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;

  // 打开文档:传入文档路径作为 instanceKey
  private openDocument(docPath: string) {
    const want: Want = {
      bundleName: 'com.example.myapp',
      abilityName: 'SpecifiedAbility',
      parameters: { instanceKey: docPath }
    };
    this.context.startAbility(want)
      .then(() => console.info('启动成功'))
      .catch((err: BusinessError) => console.error(`启动失败: ${err.message}`));
  }

  build() {
    Column({ space: 15 }) {
      Button('新建文档').onClick(() => this.openDocument(`doc_${Date.now()}`))
      Button('打开 doc_001').onClick(() => this.openDocument('doc_001'))
      Button('再次打开 doc_001').onClick(() => this.openDocument('doc_001'))
    }
  }
}

第三步:被调用方与 AbilityStage

AbilityStage 的 onAcceptWant 返回唯一 key:

onAcceptWant(want: Want): string {
  if (want.abilityName === 'SpecifiedAbility') {
    return `SpecifiedAbility_${want.parameters?.instanceKey}`;
  }
  return '';
}

SpecifiedAbility 处理新建与复用:

import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit';
import { window, AppStorage } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG = 'SpecifiedAbility';
const DOMAIN = 0x0000;

export default class SpecifiedAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    const key = `${want.parameters?.instanceKey}`;
    AppStorage.setOrCreate('instanceKey', `当前文档: ${key}`);
    hilog.info(DOMAIN, TAG, `新文档实例创建: ${key}`);
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
    const key = `${want.parameters?.instanceKey}`;
    AppStorage.setOrCreate('instanceKey', `当前文档: ${key}`);
    hilog.info(DOMAIN, TAG, `文档实例复用: ${key}`);
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/SpecifiedPage');
  }
}

页面通过 @StorageLink 双向同步标识:

@Entry
@Component
struct SpecifiedPage {
  @StorageLink('instanceKey') instanceKey: string = '当前文档: default';

  build() {
    Column({ space: 20 }) {
      Text(this.instanceKey).fontSize(24).fontColor(Color.Green)
    }
    .width('100%').height('100%').justifyContent(FlexAlign.Center)
  }
}

specified 模式执行流程

startAbility(want)
    │
    ▼
AbilityStage.onAcceptWant(want)  ──► 返回 key
    │
    ▼
系统按 key 匹配实例
    ├── 已存在 ──► onNewWant ──► onForeground
    └── 不存在 ──► onCreate ──► onWindowStageCreate ──► onForeground

七、UIAbility 组件间的交互:Want 机制

1. Want 是什么

Want 是对象间信息传递的载体,最典型的使用场景是作为 startAbility 的参数。当 UIAbility A 需要启动并传递数据给 UIAbility B 时,Want 承载目标信息与携带数据:

  • bundleName:目标 Ability 所在应用的包名
  • abilityName:Ability 名
  • parameters:自定义传递参数
  • uri:资源标识(如网址)
  • deviceId:目标设备(空或不设置表示本设备)

2. 两种启动形式

显式 Want 启动

在 Want 中明确指定 bundleNameabilityName,用于启动某个明确的 UIAbility。

const want: Want = {
  bundleName: 'com.example.myapp',
  abilityName: 'SecondAbility',
  parameters: { info: '来自 EntryAbility Index 页面' }
};
this.context.startAbility(want);

被调用方接收:

// 冷启动
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
  const info = want.parameters?.info;
  hilog.info(DOMAIN, TAG, `收到信息: ${info}`);
}

// 热启动
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
  const info = want.parameters?.info;
  hilog.info(DOMAIN, TAG, `收到信息: ${info}`);
}
隐式 Want 启动

不指定 abilityName,而是通过 entitiesactions 描述能力,由系统匹配支持该能力的应用。

被调用方在 module.json5 中声明能力:

{
  "abilities": [
    {
      "name": "BrowserAbility",
      "skills": [
        {
          "entities": ["entity.system.browsable"],
          "actions": ["ohos.want.action.viewData"]
        }
      ]
    }
  ]
}

调用方发起隐式启动(拉起浏览器并打开网址):

const want: Want = {
  action: 'ohos.want.action.viewData',
  entities: ['entity.system.browsable'],
  uri: 'https://www.huawei.com/cn'
};
this.context.startAbility(want)
  .then(() => console.info('启动成功'))
  .catch((err: BusinessError) => console.error(`启动失败: ${err.message}`));

3. 隐式 Want 的三种匹配结果

匹配结果 系统行为
未匹配到 启动失败
匹配到一个 直接启动该 UIAbility
匹配到多个 弹出选择框,提供所有满足条件的应用供用户选择

匹配完成后,可在 startAbility.then().catch() 中分别处理成功与失败的业务逻辑。


八、开发关键注意事项与避坑指南

  1. 配置生效规则:修改 module.json5(启动模式 / AbilityStage)或 AbilityStage 代码后,热重载/热更新均不生效,必须停止应用进程后重新运行项目,否则系统无法识别配置变更。
  2. AbilityStage 路径校验srcEntry 路径必须与实际文件路径完全一致,鸿蒙系统严格区分大小写,路径错误会导致 AbilityStage 初始化失败。
  3. instanceKey 判空:在 AbilityStage 的 onAcceptWant 方法中,必须对 instanceKey 做空值判断,避免因参数缺失导致标识生成失败。
  4. 标识唯一性onAcceptWant 返回的标识必须全局唯一,建议拼接 AbilityName + 业务唯一值,避免不同 Ability 的标识冲突。
  5. AppStorage 双向同步:specified 模式中,通过 AppStorage + @StorageLink 实现 Ability 与页面的状态双向同步,复用实例时页面能实时更新业务标识。
  6. onNewWant 是关键:specified / singleton 模式中,复用实例时不会触发 onCreate,所有启动参数的处理必须放在 onNewWant 方法中,否则会导致参数丢失。
  7. removeMissionAfterTerminate:建议 specified 模式的 UIAbility 设置该字段为 true,避免冷启动场景下无法复用历史任务、任务列表出现多个相同任务。
  8. singleton 重复调用:单实例模式下启动中重复调用会返回错误码 16000082,需注意调用时机。

九、内容总结

  1. 启动模式的本质是「系统对 UIAbility 实例的管理策略」,核心解决「创建新实例还是复用已有实例」的问题。
  2. 三种模式各有适用场景,核心选择依据是「业务是否需要唯一实例」和「是否需要按业务维度隔离实例」:
    • 全局唯一 → singleton
    • 每次新建 → multiton
    • 灵活复用 → specified
  3. AbilityStage 是模块级生命周期管理器,也是 specified 模式的必要条件,其核心作用是为 specified 模式生成实例唯一标识。
  4. specified 模式核心逻辑:启动传参 instanceKey → AbilityStage 生成唯一标识 → 系统按标识匹配/复用实例 → 复用触发 onNewWant
  5. 生命周期联动核心规则:创建实例触发 onCreate,复用实例触发 onNewWantonWindowStageCreate / onForeground 等方法不受启动模式影响,按需触发。
  6. Want 机制是 UIAbility 间通信的桥梁,显式 Want 用于启动明确目标,隐式 Want 用于按能力匹配,覆盖「不关心提供者是谁,只关心能力」的场景。
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐