[鸿蒙从零到一] Stage 模型与工程结构详解
前言
在 HarmonyOS 应用开发中,理解项目结构和运行模型是写好代码的前提。HarmonyOS NEXT 全面采用 Stage 模型,这是区别于旧版 FA 模型的全新架构。本文将从一个新建的 HarmonyOS 项目出发,带你深入理解 Stage 模型的设计思想和工程组织的每一个细节。
一、为什么需要 Stage 模型?
在早期的 HarmonyOS 开发中,使用的是 FA(Feature Ability)模型。FA 模型将 Ability 作为最小独立单元,每个 Ability 拥有独立的配置文件和运行上下文。这在小型应用中尚可,但随着应用规模扩大,FA 模型暴露出以下问题:
- 配置分散:每个 Ability 都要在
config.json中声明,多人协作时容易产生冲突 - 资源冗余:Ability 之间难以共享全局资源
- 权限管理粒度粗:难以做到组件级别的权限隔离
Stage 模型正是为了解决这些问题而生。它引入了"阶段化"(Staged)的生命周期管理,将应用配置集中化,组件职责更加清晰。
二、新建一个 Stage 模型项目
使用 DevEco Studio 创建一个空项目,选择 Empty Ability 模板,生成的目录结构如下:
MyHarmonyApp/
├── AppScope/ # 应用全局配置
│ ├── app.json5 # 应用级配置(bundleName、versionCode 等)
│ └── resources/ # 全局公共资源(图标等)
├── entry/ # 主模块(默认入口模块)
│ ├── src/main/
│ │ ├── module.json5 # 模块级配置
│ │ ├── ets/ # ArkTS 源码目录
│ │ │ ├── entryability/ # Ability 实现
│ │ │ │ └── EntryAbility.ets
│ │ │ └── pages/ # 页面文件
│ │ │ └── Index.ets
│ │ └── resources/ # 模块级资源
│ ├── oh-package.json5 # 模块依赖配置
│ └── build-profile.json5 # 构建配置
├── build-profile.json5 # 项目级构建配置
└── oh-package.json5 # 项目级依赖配置
关键区别:AppScope vs entry
| 层级 | 作用 | 典型内容 |
|---|---|---|
| AppScope | 应用级别,跨模块共享 | bundleName、应用图标、全局资源 |
| entry | 模块级别,具体的功能实现 | 页面、Ability、模块资源 |
一个应用可以包含多个模块(HAP/HSP/HAR),但 AppScope 只有一个。
三、核心配置文件解析
3.1 app.json5 —— 应用级配置
{
"app": {
"bundleName": "com.example.myharmonyapp",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:app_icon",
"label": "$string:app_name",
"distributedNotificationEnabled": true
}
}
这里定义了应用的"身份证"——包名、版本号、图标等。bundleName 是应用的唯一标识,上架后不可更改。
3.2 module.json5 —— 模块级配置
这是 Stage 模型最重要的配置文件,替代了旧版的 config.json:
{
"module": {
"name": "entry",
"type": "entry", // 模块类型:entry / feature / har / hsp
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": ["phone", "tablet", "2in1"],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color: startWindowBgColor",
"exported": true,
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["action.system.home"]
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
核心字段解读:
type:模块类型。entry是入口模块(必须有且只有一个),feature是功能模块,har/hsp是共享库模块mainElement:指定默认启动的 Abilitypages:引用路由配置文件,管理页面跳转abilities:声明当前模块的所有 AbilityrequestPermissions:声明模块需要的权限
3.3 路由配置 main_pages.json
在 src/main/resources/base/profile/ 下:
{
"src": [
"pages/Index",
"pages/Second"
]
}
所有需要跳转的页面都必须在这里注册,否则 router.pushUrl 会找不到目标页面。
四、Stage 模型的四大组件
Stage 模型定义了四种应用组件,各有分工:
4.1 UIAbility —— 有界面的能力组件
这是最常用的组件,承载应用的页面和交互逻辑:
import {
AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import {
window } from '@kit.ArkUI';
import {
hilog } from '@kit.PerformanceAnalysisKit';
const TAG: string = 'EntryAbility';
const DOMAIN: number = 0xFF00;
export default class EntryAbility extends UIAbility {
// 创建时调用,初始化数据
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(DOMAIN, TAG, 'onCreate');
}
// 窗口创建后调用,加载页面
onWindowStageCreate(windowStage: window.WindowStage): void {
hilog.info(DOMAIN, TAG, 'onWindowStageCreate');
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(DOMAIN, TAG, 'Failed to load content: %{public}s', JSON.stringify(err));
return;
}
hilog.info(DOMAIN, TAG, 'Content loaded successfully');
});
}
// 前台显示时调用
onForeground(): void {
hilog.info(DOMAIN, TAG, 'onForeground');
}
// 后台隐藏时调用
onBackground(): void {
hilog.info(DOMAIN, TAG, 'onBackground');
}
// 销毁时调用
onDestroy(): void {
hilog.info(DOMAIN, TAG, 'onDestroy');
}
}
4.2 ExtensionAbility —— 扩展能力组件
用于实现系统级扩展功能,如输入法、卡片服务、无障碍服务等:
import {
ExtensionAbility } from '@kit.AbilityKit';
export default class MyExtension extends ExtensionAbility {
onCreate(want: Want): void {
// 扩展能力初始化
}
onRequest(want: Want, startId: number): void {
// 处理请求
}
}
4.3 ServiceExtensionAbility —— 后台服务组件
提供跨进程的后台服务能力(仅系统应用可用):
import {
ServiceExtensionAbility } from '@kit.AbilityKit';
export default class MyService extends ServiceExtensionAbility {
onCreate(want: Want): void {
// 服务初始化
}
onConnect(want: Want): rpc.RemoteObject {
// 返回远程通信对象
return new MyRemoteObject('MyService');
}
}
4.4 FormExtensionAbility —— 卡片服务组件
用于实现桌面卡片(Widget)功能:
import {
formBindingData, FormExtensionAbility } from '@kit.FormKit';
export default class MyForm extends FormExtensionAbility {
onAddForm(want: Want): formBindingData.FormBindingData {
const formData = {
title: '鸿蒙卡片', content: '欢迎使用' };
return formBindingData.createFormBindingData(formData);
}
onUpdateForm(formId: string): formBindingData.FormBindingData {
// 更新卡片数据
return formBindingData.createFormBindingData({
title: '已更新' });
}
}
五、UIAbility 生命周期详解
UIAbility 的生命周期是 Stage 模型的核心,理解它才能正确管理资源:
┌──────────┐
│ Create │ ← 实例创建,初始化数据
└────┬─────┘
│
┌────▼─────────────┐
│ WindowStageCreate │ ← 窗口就绪,加载页面
└────┬─────────────┘
│
┌───────▼───────┐
│ Foreground │ ←→ 应用进入前台 / 退到后台
│ Background │
└───────┬───────┘
│
┌────▼─────────────┐
│ WindowStageDestroy│ ← 窗口销毁
└────┬─────────────┘
│
┌────▼─────┐
│ Destroy │ ← 实例销毁,释放资源
└──────────┘
关键生命周期回调:
| 回调 | 时机 | 典型操作 |
|---|---|---|
onCreate |
Ability 实例创建 | 初始化全局变量、注册监听 |
onWindowStageCreate |
窗口 Stage 创建完成 | loadContent 加载页面 |
onForeground |
从后台切回前台 | 恢复暂停的任务(动画、轮询) |
onBackground |
从前台切到后台 | 暂停耗时任务、保存状态 |
onWindowStageDestroy |
窗口即将销毁 | 释放 UI 相关资源 |
onDestroy |
Ability 即将销毁 | 释放所有资源、取消监听 |
冷启动 vs 热启动
- 冷启动:完整经历
onCreate → onWindowStageCreate → onForeground - 热启动(从后台返回):只触发
onForeground,不重新创建
这意味着在 onCreate 中做的初始化不会因为热启动而重复执行,但在 onForeground 中的数据刷新每次都会触发。
六、模块类型与依赖关系
Stage 模型支持四种模块类型,适用于不同的工程化需求:
6.1 entry(入口模块)
- 每个应用有且仅有一个
- 包含
mainElement指定的启动 Ability - 可以安装到设备
6.2 feature(功能模块)
- 按需加载的功能模块
- 可以有自己的 Ability 和页面
- 适合大型应用的模块化拆分
6.3 HAR(静态共享包)
- 编译时打包到引用方
- 代码会被复制一份到使用方
- 适合工具类、基础组件库
6.4 HSP(动态共享包)
- 运行时共享,不会重复打包
- 减小应用包体积
- 适合跨模块共享的大型组件
模块配置示例(oh-package.json5):
{
"name": "entry",
"version": "1.0.0",
"description": "主入口模块",
"main": "",
"author": "",
"license": "",
"dependencies": {
"@ohos/mylib": "file:../mylib", // 本地 HAR 依赖
"@ohos/shared-ui": "file:../shared-ui" // 本地 HSP 依赖
}
}
七、上下文(Context)体系
Stage 模型重新设计了上下文体系,不同层级有不同的 Context:
ApplicationContext ← 应用级,全局唯一
└── UIAbilityContext ← Ability 级,每个 Ability 一个
└── Context ← 页面/组件级
常用 Context 获取方式
// 在 UIAbility 中
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
const context = this.context; // UIAbilityContext
context.filesDir; // 沙箱文件目录
context.cacheDir; // 缓存目录
context.resourceManager; // 资源管理器
}
}
// 在页面组件中
@Entry
@Component
struct Index {
build() {
Column() {
Button('获取上下文')
.onClick(() => {
const context = getContext(this) as common.UIAbilityContext;
console.info('Files dir: ' + context.filesDir);
})
}
}
}
Context 能做什么?
| 能力 | 方法 | 说明 |
|---|---|---|
| 文件访问 | filesDir / cacheDir / tempDir |
获取沙箱路径 |
| 资源管理 | resourceManager |
获取字符串、图片等资源 |
| 权限管理 | requestPermissionsFromUser |
动态申请权限 |
| 跨模块调用 | startAbility |
启动其他 Ability |
| 数据共享 | preferences |
轻量级键值存储 |
八、Want 机制:组件间通信的桥梁
Want 是 Stage 模型中组件间传递信息的载体,类似于 Android 的 Intent:
// 启动另一个 Ability
const want: Want = {
bundleName: 'com.example.myharmonyapp',
abilityName: 'SecondAbility',
parameters: {
userId: '12345',
action: 'view'
}
};
const context = getContext(this) as common.UIAbilityContext;
context.startAbility(want).then(() => {
console.info('Ability started successfully');
}).catch((err: Error) => {
console.error('Failed to start: ' + err.message);
});
接收端获取参数:
export default class SecondAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
const userId = want.parameters?.userId as string;
console.info('Received userId: ' + userId);
}
}
隐式 Want
不指定具体 Ability,而是通过 action 和 entities 让系统匹配:
const implicitWant: Want = {
action: 'action.view.image',
entities: ['entity.system.default'],
uri: 'file:///data/storage/photo.jpg'
};
context.startAbility(implicitWant);
系统会根据 module.json5 中声明的 skills 匹配最合适的 Ability 来处理。
九、进程模型与线程模型
进程模型
Stage 模型下,一个应用默认运行在一个进程中,但可以通过配置实现多进程:
// module.json5
{
"module": {
"abilities": [
{
"name": "HeavyAbility",
"process": "com.example.heavy", // 指定运行在独立进程
"srcEntry": "./ets/HeavyAbility.ets"
}
]
}
}
线程模型
HarmonyOS 使用 Worker 和 TaskPool 两种多线程方案:
// TaskPool:推荐方式,自动管理线程池
import {
taskpool } from '@kit.ArkTS';
@Concurrent
function heavyCompute(data: number[]): number {
return data.reduce((sum, val) => sum + val, 0);
}
// 在主线程中调用
const result = await taskpool.execute(heavyCompute, [1, 2, 3, 4, 5]);
console.info('Result: ' + result);
十、工程化最佳实践
10.1 合理的模块拆分
MyApp/
├── AppScope/ # 全局配置
├── entry/ # 入口模块(壳工程)
├── feature_home/ # 首页功能模块
├── feature_profile/ # 个人中心模块
├── lib_network/ # 网络请求 HAR
├── lib_base/ # 基础工具 HAR
└── shared_ui/ # 共享 UI 组件 HSP
10.2 统一的资源管理
resources/
├── base/ # 默认资源
│ ├── element/
│ │ ├── color.json # 颜色定义
│ │ └── string.json # 字符串
│ ├── media/ # 图片资源
│ └── profile/ # 配置文件
├── en_US/ # 英文适配
├── zh_CN/ # 中文资源
└── dark/ # 暗色模式资源
10.3 环境配置管理
使用 build-profile.json5 管理多环境:
{
"app": {
"products": [
{
"name": "default",
"signingConfig": "default",
"compileSdkVersion": 5.0.0(12),
"compatibleSdkVersion": 5.0.0(12)
}
]
},
"modules": [
{
"name": "entry",
"srcPath": "./entry",
"targets": [
{ "name": "default", "applyToProducts": ["default"] }
]
}
]
}
总结
Stage 模型是 HarmonyOS 应用开发的基石。通过本文的学习,你应该掌握了:
- Stage 模型的设计动机——解决 FA 模型的配置分散和资源冗余问题
- 工程目录结构——AppScope 与模块的分层设计
- 核心配置文件——app.json5、module.json5、路由配置的作用
- 四大组件——UIAbility、ExtensionAbility、ServiceExtensionAbility、FormExtensionAbility
- 生命周期管理——冷启动/热启动的回调时序
- 模块类型——entry、feature、HAR、HSP 的选择策略
- Context 体系——不同层级上下文的获取和使用
- Want 机制——组件间通信的显式与隐式调用
理解了这些概念,你就为后续的页面开发、状态管理、数据持久化打下了坚实的基础。下一步,我们将深入 Ability 的具体使用场景和生命周期中的实战技巧。
本文基于 HarmonyOS NEXT(API 12+)和 DevEco Studio 5.0+,内容可能随版本更新而变化。