HarmonyOS | UIAbility 进阶
一、前言
在 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
AbilityStage 是 Module 级别 的组件容器,用于管理该 Module 中的 UIAbility 和 ExtensionAbility。它与编译期的 HAP 一一对应。
5. 运行期与编译期关系
| 维度 | 编译期 | 运行期 |
|---|---|---|
| 应用 | App | Application |
| 模块 | HAP | AbilityStage |
| 组件 | UIAbility / ExtensionAbility | UIAbility 实例 / ExtensionAbility 实例 |
运行期为 Application、AbilityStage、UIAbility、ExtensionAbility 分别提供对应的上下文环境:ApplicationContext、AbilityStageContext、UIAbilityContext、ExtensionAbilityContext,开发者可通过上下文调用各种资源和能力。
三、UIAbility 与 WindowStage 的生命周期联动
UIAbility 组件和 WindowStage 各自拥有一套生命周期。在设备上首次启动某个 UIAbility 时,三者的执行顺序如下:
- 系统首先创建持有该 UIAbility 的 AbilityStage 实例,创建成功后执行其
onCreate回调(可做 Module 级资源初始化)。 - 系统为该 UIAbility 创建实例,创建成功后执行其
onCreate回调(变量定义、资源加载等)。 - 系统为 UIAbility 实例创建一个 WindowStage 实例(一一对应),创建成功后执行
onWindowStageCreate回调(加载 UI、订阅窗口事件)。 - UIAbility 进入前台,执行
onForeground。 - 销毁前先执行
onWindowStageDestroy(释放 UI 资源),再执行onDestroy。
这种松耦合设计的好处
- 业务逻辑与 UI 逻辑分离:UIAbility 处理与页面无关的业务(蓝牙、数据库),WindowStage 上的 ArkUI 处理界面逻辑。
- 便于系统裁剪:无屏设备运行应用时不会创建窗口模块,减少 ROM 占用。
- 多设备复用同一套生命周期:系统自动判断设备形态并执行对应的窗口生命周期流程。
单窗口 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.json5中launchType: "singleton"(默认值,可省略)。 - 适用场景:应用主界面、个人中心、购物车等需要全局唯一状态的页面。
- 生命周期:首次启动走
onCreate→onWindowStageCreate→onForeground;后续启动仅触发onNewWant→onForeground。
2. multiton(多实例模式)
- 定义:每次启动都创建一个新实例,实例之间相互独立。
- 配置:
launchType: "multiton"(旧名standard,功能一致)。 - 适用场景:浏览器多标签页、多窗口文档编辑、多任务处理。
- 生命周期:每次启动都完整走
onCreate→onWindowStageCreate→onForeground,任务列表中可见多个任务。
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 中将 SpecifiedAbility 的 launchType 设置为 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 中明确指定 bundleName 和 abilityName,用于启动某个明确的 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,而是通过 entities 和 actions 描述能力,由系统匹配支持该能力的应用。
被调用方在 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() 中分别处理成功与失败的业务逻辑。
八、开发关键注意事项与避坑指南
- 配置生效规则:修改
module.json5(启动模式 / AbilityStage)或 AbilityStage 代码后,热重载/热更新均不生效,必须停止应用进程后重新运行项目,否则系统无法识别配置变更。 - AbilityStage 路径校验:
srcEntry路径必须与实际文件路径完全一致,鸿蒙系统严格区分大小写,路径错误会导致 AbilityStage 初始化失败。 - instanceKey 判空:在 AbilityStage 的
onAcceptWant方法中,必须对instanceKey做空值判断,避免因参数缺失导致标识生成失败。 - 标识唯一性:
onAcceptWant返回的标识必须全局唯一,建议拼接AbilityName + 业务唯一值,避免不同 Ability 的标识冲突。 - AppStorage 双向同步:specified 模式中,通过
AppStorage+@StorageLink实现 Ability 与页面的状态双向同步,复用实例时页面能实时更新业务标识。 - onNewWant 是关键:specified / singleton 模式中,复用实例时不会触发
onCreate,所有启动参数的处理必须放在onNewWant方法中,否则会导致参数丢失。 - removeMissionAfterTerminate:建议 specified 模式的 UIAbility 设置该字段为
true,避免冷启动场景下无法复用历史任务、任务列表出现多个相同任务。 - singleton 重复调用:单实例模式下启动中重复调用会返回错误码
16000082,需注意调用时机。
九、内容总结
- 启动模式的本质是「系统对 UIAbility 实例的管理策略」,核心解决「创建新实例还是复用已有实例」的问题。
- 三种模式各有适用场景,核心选择依据是「业务是否需要唯一实例」和「是否需要按业务维度隔离实例」:
- 全局唯一 → singleton
- 每次新建 → multiton
- 灵活复用 → specified
- AbilityStage 是模块级生命周期管理器,也是 specified 模式的必要条件,其核心作用是为 specified 模式生成实例唯一标识。
- specified 模式核心逻辑:启动传参
instanceKey→ AbilityStage 生成唯一标识 → 系统按标识匹配/复用实例 → 复用触发onNewWant。 - 生命周期联动核心规则:创建实例触发
onCreate,复用实例触发onNewWant;onWindowStageCreate/onForeground等方法不受启动模式影响,按需触发。 - Want 机制是 UIAbility 间通信的桥梁,显式 Want 用于启动明确目标,隐式 Want 用于按能力匹配,覆盖「不关心提供者是谁,只关心能力」的场景。
更多推荐




所有评论(0)