HarmonyOS Ability 基础与生命周期实战
在 HarmonyOS 应用开发里,Ability 是承载所有可运行能力的最小单元。不管是启动一个页面、跑一段后台服务,还是接受外部拉起,几乎都会落在某个 Ability 上。理解 Ability 的定位、类型与生命周期,是把 Stage 模型玩转的基础。这篇文章会从概念到源码,由浅入深地把 Ability 讲透。
一、Ability 是什么
一句话:Ability 是 HarmonyOS 应用暴露给系统的“可被调度的能力入口”。
- 系统视角:系统调度器根据用户操作或事件(点击图标、通知拉起、任务列表、语音助手、原子化服务等)挑一个 Ability 拉起来。
- 应用视角:应用把自己拆成若干可独立启动、可被外部访问的 Ability,各自有独立的上下文、独立的生命周期。
Stage 模型(当前 HarmonyOS 推荐模型)下,Ability 有两大类:
| 类型 | 类名 | 定位 |
|---|---|---|
| UIAbility | UIAbility |
带界面的能力,承载页面栈,用户可见 |
| ExtensionAbility | ServiceExtensionAbility、DataShareExtensionAbility 等 |
无界面的扩展能力,专注后台服务、数据共享、卡片、输入法等场景 |
初学阶段主要打交道的是 UIAbility;ExtensionAbility 用到再展开。
二、Ability、AbilityStage 和 Module 的关系
Stage 模型里,一个 HAP 模块里可以有多个 Ability,它们共享一个 AbilityStage:
HAP Module
├── AbilityStage // 模块级容器,模块首次加载时创建
│ ├── UIAbility A // 独立进程/独立任务栈
│ ├── UIAbility B
│ └── ServiceExtensionAbility C
└── resources / pages / ets ...
AbilityStage是模块运行时的容器,负责模块加载时的初始化,比如注入依赖、初始化路由、埋点 SDK。- 每个
UIAbility有独立的任务(Task),可以在最近任务列表里独立出现。 - 不同 Ability 之间通过
Want通信,不能直接共享内存里的 UI 状态。
三、UIAbility 生命周期状态机
UIAbility 的生命周期比 Android Activity 更规整,主要有五个回调:
onCreate
│
▼
onWindowStageCreate
│
▼
onForeground
│
(可见 & 可交互)
│
▼
onBackground
│
(切后台,仍存活)
│
▼
onWindowStageDestroy
│
▼
onDestroy
各回调职责:
onCreate(want, launchParam):Ability 首次创建时触发。做一次性初始化、读取启动参数。注意:此时 UI 还没有 WindowStage,不要在这里操作窗口。onWindowStageCreate(windowStage):窗口舞台创建完成。加载首页、设置沉浸式、监听窗口事件,都在这里做。onForeground():Ability 进入前台,用户可见可交互。启动定时刷新、恢复动画。onBackground():Ability 切到后台,用户不可见但进程还在。暂停耗电任务、保存临时状态。onWindowStageDestroy():窗口舞台销毁前的最后机会,释放和 WindowStage 绑定的资源。onDestroy():Ability 生命周期终点,做最终清理。
关键点:onCreate ↔ onDestroy 是一对;onWindowStageCreate ↔ onWindowStageDestroy 是一对;onForeground ↔ onBackground 会反复来回。
四、一个最小可运行的 UIAbility
新建工程后,entry/src/main/ets/entryability/EntryAbility.ets 里的默认代码大概长这样,我们在里面加上完整的生命周期日志:
import UIAbility from '@ohos.app.ability.UIAbility';
import type Want from '@ohos.app.ability.Want';
import type AbilityConstant from '@ohos.app.ability.AbilityConstant';
import window from '@ohos.window';
import hilog from '@ohos.hilog';
const TAG = 'EntryAbility';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0000, TAG, 'onCreate, action=%{public}s', want.action ?? '');
// 读取启动参数
const from = want.parameters?.['from'] as string | undefined;
hilog.info(0x0000, TAG, 'launched by: %{public}s', from ?? 'launcher');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
hilog.info(0x0000, TAG, 'onWindowStageCreate');
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(0x0000, TAG, 'loadContent failed: %{public}s', JSON.stringify(err));
return;
}
hilog.info(0x0000, TAG, 'loadContent ok');
});
}
onForeground(): void {
hilog.info(0x0000, TAG, 'onForeground');
}
onBackground(): void {
hilog.info(0x0000, TAG, 'onBackground');
}
onWindowStageDestroy(): void {
hilog.info(0x0000, TAG, 'onWindowStageDestroy');
}
onDestroy(): void {
hilog.info(0x0000, TAG, 'onDestroy');
}
}
跑起来后,从桌面点击图标进入,日志顺序会是:
onCreate → onWindowStageCreate → onForeground
按 Home 返回桌面:
onBackground
再次点击图标切回:
onForeground
系统真正回收进程时(比如从最近任务列表滑掉、内存紧张被杀):
onBackground → onWindowStageDestroy → onDestroy
五、Ability 之间的启动与参数传递
UIAbility 之间不共享内存,跳转靠 Want。Want 就是一份跨 Ability 的启动意图描述。
5.1 启动另一个 UIAbility
import common from '@ohos.app.ability.common';
import type Want from '@ohos.app.ability.Want';
@Entry
@Component
struct Index {
build() {
Column() {
Button('打开详情页 Ability')
.onClick(() => {
const ctx = getContext(this) as common.UIAbilityContext;
const want: Want = {
bundleName: 'com.example.harmonydemo',
abilityName: 'DetailAbility',
parameters: {
itemId: 1024,
from: 'index',
},
};
ctx.startAbility(want).catch((err: Error) => {
console.error(`startAbility failed: ${
err.message}`);
});
})
}
.width('100%').height('100%')
}
}
对面的 DetailAbility.onCreate 就能拿到 want.parameters.itemId。
5.2 带返回值的启动
需要拿到结果时,改用 startAbilityForResult:
const result = await ctx.startAbilityForResult({
bundleName: 'com.example.harmonydemo',
abilityName: 'PickerAbility',
});
// result.resultCode / result.want.parameters
if (result.resultCode === 0) {
const picked = result.want?.parameters?.['picked'] as string | undefined;
console.info(`picked=${
picked}`);
}
被启动的 Ability 在结束前调用:
this.context.terminateSelfWithResult({
resultCode: 0,
want: {
parameters: {
picked: 'apple' },
},
});
5.3 启动模式
在 module.json5 里通过 launchType 控制:
| launchType | 语义 |
|---|---|
singleton |
全局单实例,再次启动复用 |
multiton |
每次启动都新建实例 |
specified |
由 onAcceptWant 决定实例的 key,自定义复用逻辑 |
大多数首页/主入口用 singleton;文档编辑器这种“每个文档一个 Ability 实例”的场景用 specified。
六、AbilityStage:模块级初始化的正确位置
同一个 HAP 里若干 Ability 共用一个 AbilityStage,非常适合放模块级初始化:
import AbilityStage from '@ohos.app.ability.AbilityStage';
import hilog from '@ohos.hilog';
export default class EntryAbilityStage extends AbilityStage {
onCreate(): void {
hilog.info(0x0000, 'AbilityStage', 'module loaded');
// 初始化:埋点 SDK、Crash 上报、全局路由表、依赖注入容器等
}
onAcceptWant(want): string {
// launchType=specified 时决定实例 key
return want.parameters?.['instanceKey'] as string ?? 'default';
}
}
在 module.json5 中注册:
{
"module": {
"srcEntry": "./ets/entryability/EntryAbilityStage.ets",
"abilities": [ /* ... */ ]
}
}
不要把这些初始化塞进每个 UIAbility 的 onCreate,否则多入口场景会重复执行。
七、常见坑与调试建议
- onCreate 里操作 UI:此时 WindowStage 还没准备好,操作窗口会 crash。UI 相关放
onWindowStageCreate。 - 在 onBackground 做重活:切后台后系统随时可能回收进程,长任务应该走后台任务能力,而不是在这里
await一个大同步操作。 - Ability 间共享数据:不要靠单例静态变量,跨进程时不可靠。优先用
Want.parameters传参、Preferences或relationalStore持久化、EventHub/CommonEvent广播。 - 窗口大小监听:折叠屏、平板需要监听
windowStage.on('windowStageEvent', ...)和window.on('windowSizeChange', ...),在onWindowStageCreate注册、onWindowStageDestroy反注册。 - 调试生命周期:
hdc shell aa dump -a可以看到当前进程里所有 Ability 状态;hdc shell aa start -a AbilityName -b BundleName可以在命令行直接拉起,方便复现。
八、小结
Ability 是 HarmonyOS 应用的运行时骨架,掌握它需要抓住三件事:
- 分层清晰:AbilityStage 管模块级别,UIAbility 管一个可见入口,ExtensionAbility 管无界面能力。
- 生命周期节奏:onCreate → onWindowStageCreate → onForeground 是启动主线;onBackground / onForeground 会反复;onWindowStageDestroy → onDestroy 是终点。
- 通信统一走 Want:跨 Ability、跨应用、带结果回调,全都是
Want描述 +startAbility*系列 API。
打好这层地基,后面做页面路由、多入口、原子化服务、卡片能力时才不会脚下打滑。