本文献给:
已掌握 UI 构建、状态管理和生命周期的鸿蒙开发者。本文将深入讲解 HarmonyOS 工程的目录结构、核心配置文件(app.json5 和 module.json5)的详细含义与用法,以及 Stage 模型下 UIAbility 的生命周期、三种启动模式和 Want 跨 Ability 跳转机制。掌握这些知识后,你将具备从项目全局视角理解和配置应用的能力。
你将学到:
- 鸿蒙工程级目录结构中各文件夹的职责
app.json5应用全局配置详解module.json5模块配置详解(Ability 声明、权限、窗口模式)- Ability 的概念与 UIAbility 生命周期
- singleton、multiton、specified 三种启动模式的区别与配置
- Want 的结构与显式/隐式跳转及参数传递
目录
- 一、工程级目录结构详解
-
- [1.1 各目录职责速览](#1.1 各目录职责速览)
- [1.2 资源目录区分:base、rawfile 与 element](#1.2 资源目录区分:base、rawfile 与 element)
- [二、核心配置文件详解 ------ app.json5](#二、核心配置文件详解 —— app.json5)
-
- [2.1 完整示例](#2.1 完整示例)
- [2.2 关键字段说明](#2.2 关键字段说明)
- [三、核心配置文件详解 ------ module.json5](#三、核心配置文件详解 —— module.json5)
-
- [3.1 完整示例](#3.1 完整示例)
- [3.2 关键字段说明](#3.2 关键字段说明)
- [3.3 main_pages.json 路由文件](#3.3 main_pages.json 路由文件)
- [四、Ability 概念与 UIAbility 生命周期](#四、Ability 概念与 UIAbility 生命周期)
-
- [4.1 什么是 Ability](#4.1 什么是 Ability)
- [4.2 UIAbility 生命周期](#4.2 UIAbility 生命周期)
- [4.3 生命周期回调触发时机](#4.3 生命周期回调触发时机)
- [4.4 典型生命周期流程](#4.4 典型生命周期流程)
- [五、启动模式 ------ singleton、multiton、specified](#五、启动模式 —— singleton、multiton、specified)
-
- [5.1 singleton ------ 单实例模式(默认)](#5.1 singleton —— 单实例模式(默认))
- [5.2 multiton ------ 多实例模式](#5.2 multiton —— 多实例模式)
- [5.3 specified ------ 指定实例模式](#5.3 specified —— 指定实例模式)
- [5.4 三种模式对比](#5.4 三种模式对比)
- [六、Want 详解与跨 Ability 跳转](#六、Want 详解与跨 Ability 跳转)
-
- [6.1 Want 的结构](#6.1 Want 的结构)
- [6.2 显式 Want 跳转](#6.2 显式 Want 跳转)
- [6.3 隐式 Want 跳转](#6.3 隐式 Want 跳转)
- [6.4 startAbilityForResult 获取返回结果](#6.4 startAbilityForResult 获取返回结果)
- [七、综合示例 ------ 多 Ability 跳转与模式测试](#七、综合示例 —— 多 Ability 跳转与模式测试)
- 八、常见错误与注意事项
-
- [8.1 页面未在 main_pages.json 中注册](#8.1 页面未在 main_pages.json 中注册)
- [8.2 Ability 未在 module.json5 中声明](#8.2 Ability 未在 module.json5 中声明)
- [8.3 启动模式配置不匹配预期行为](#8.3 启动模式配置不匹配预期行为)
- [8.4 隐式 Want 匹配失败](#8.4 隐式 Want 匹配失败)
- [8.5 rawfile 路径错误](#8.5 rawfile 路径错误)
- 九、小结
一、工程级目录结构详解
使用 DevEco Studio 创建一个 Stage 模型的 HarmonyOS 项目后,目录结构如下:
MyApplication
├── AppScope
│ ├── resources
│ │ └── base
│ │ ├── element
│ │ └── media
│ └── app.json5
├── entry
│ ├── src
│ │ ├── main
│ │ │ ├── ets
│ │ │ │ ├── entryability
│ │ │ │ │ └── EntryAbility.ts
│ │ │ │ └── pages
│ │ │ │ └── Index.ets
│ │ │ ├── resources
│ │ │ │ ├── base
│ │ │ │ │ ├── element
│ │ │ │ │ ├── media
│ │ │ │ │ └── profile
│ │ │ │ │ └── main_pages.json
│ │ │ │ └── rawfile
│ │ │ └── module.json5
│ │ ├── ohosTest
│ │ │ └── ...
│ │ └── test
│ │ └── ...
│ ├── build-profile.json5
│ └── hvigorfile.ts
├── hvigor
│ └── hvigor-config.json5
├── oh_modules
├── build-profile.json5
└── hvigorfile.ts
1.1 各目录职责速览
| 目录 / 文件 | 职责 |
|---|---|
AppScope/ |
应用全局配置与资源,独立于模块,包含 app.json5 和全局图标等 |
AppScope/app.json5 |
应用级别配置:包名、版本、图标、名称等 |
entry/ |
主模块(HAP),应用的功能代码和资源主要放在此 |
entry/src/main/ets/ |
ArkTS 源码目录,包含 Ability 和 pages 等 |
entry/src/main/ets/entryability/ |
UIAbility 代码,应用的入口 Ability |
entry/src/main/ets/pages/ |
页面组件目录 |
entry/src/main/resources/ |
模块级资源:字符串、图片、布局文件等 |
entry/src/main/resources/base/profile/ |
配置文件,如路由页面列表 main_pages.json |
entry/src/main/resources/rawfile/ |
原始文件(任意格式,不会被编译),运行时按原样读取 |
entry/src/main/module.json5 |
模块配置文件:声明 Ability、权限、窗口模式等 |
hvigor/ |
构建工具 hvigor 的配置文件 |
oh_modules/ |
依赖包存储目录,类比 node_modules |
build-profile.json5(根目录) |
应用级构建配置(编译 SDK 版本、签名等) |
build-profile.json5(模块内) |
模块级构建配置 |
1.2 资源目录区分:base、rawfile 与 element
resources/base/:存放需要适配的资源,可扩展多语言/多分辨率目录(如zh_CN、en_US),系统根据设备配置自动匹配。resources/base/media/:图片、音频等媒体文件,通过$r('app.media.filename')引用。resources/base/element/:字符串、颜色、尺寸等,通过$r('app.string.name')引用,支持国际化。resources/base/profile/:JSON 配置文件(如main_pages.json),通过$profile:main_pages引用。resources/rawfile/:原始文件目录,文件会原样打包到应用中,运行时通过resourceManager.getRawFileContent()读取,适用于大文本、配置文件等无需编译的资源。
二、核心配置文件详解 ------ app.json5
app.json5 位于 AppScope/app.json5,定义了应用级别的全局信息。
2.1 完整示例
json5
{
"app": {
"bundleName": "com.example.myapp", // 应用包名(全局唯一标识)
"vendor": "example", // 开发者/供应商名称
"versionCode": 1, // 版本号(整数,用于版本对比)
"versionName": "1.0.0", // 版本名(面向用户)
"icon": "$media:app_icon", // 应用图标(引用 resources 下的图片)
"label": "$string:app_name", // 应用名称(引用字符串资源,支持多语言)
"targetAPIVersion": 11, // 目标 API 版本
"compatibleAPIVersion": 9, // 最低兼容 API 版本
"debug": false, // 是否为调试版本
"car": { // 车载设备配置(可选)
"minAPIVersion": 9
}
}
}
2.2 关键字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
bundleName |
string | 应用唯一标识,采用反向域名格式,安装后不可变更 |
vendor |
string | 开发者/供应商名称 |
versionCode |
number | 版本号,整数,用于系统判断版本新旧 |
versionName |
string | 面向用户的版本字符串 |
icon |
resource | 应用图标,引用 media 下的图片资源 |
label |
resource | 应用名称,引用 string 资源以实现多语言 |
targetAPIVersion |
number | 目标 API 版本,表示应用针对此版本开发 |
compatibleAPIVersion |
number | 兼容的最低 API 版本,低于此版本的设备无法安装 |
debug |
boolean | 是否为调试版本,影响签名和调试行为 |
三、核心配置文件详解 ------ module.json5
module.json5 位于 entry/src/main/module.json5,是模块级别的配置文件,定义了 Ability、权限、窗口行为等。
3.1 完整示例
json5
{
"module": {
"name": "entry", // 模块名
"type": "entry", // 模块类型:entry(主模块)/ feature(动态特性)
"description": "$string:module_desc", // 模块描述
"mainElement": "EntryAbility", // 入口 Ability 名称
"deviceTypes": [ // 支持的设备类型
"phone",
"tablet"
],
"deliveryWithInstall": true, // 是否随应用安装(false 时可按需下载)
"installationFree": false, // 是否免安装
"pages": "$profile:main_pages", // 页面路由配置文件
"abilities": [ // Ability 声明列表
{
"name": "EntryAbility", // Ability 名称
"srcEntry": "./ets/entryability/EntryAbility.ts", // 代码入口
"description": "$string:entry_ability_desc",
"icon": "$media:ability_icon",
"label": "$string:entry_ability_label",
"startWindowIcon": "$media:start_icon",
"startWindowBackground": "$color:start_window_background",
"exported": true, // 是否可被其他应用调用
"launchType": "singleton", // 启动模式
"skills": [ // Want 匹配规则
{
"actions": ["action.system.home"],
"entities": ["entity.system.home"]
}
]
}
],
"requestPermissions": [ // 权限声明
{
"name": "ohos.permission.INTERNET",
"reason": "$string:internet_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
],
"window": { // 窗口配置
"designWidth": 720,
"autoDesignWidth": true
}
}
}
3.2 关键字段说明
| 字段 | 说明 |
|---|---|
name |
模块名称 |
type |
模块类型:entry(主模块,必有)、feature(动态模块,可延迟下载) |
mainElement |
入口 Ability 名称,应用启动时首先加载此 Ability |
deviceTypes |
支持的设备类型数组 |
pages |
页面路由列表,指向 resources/base/profile/ 下的 JSON 文件 |
abilities |
Ability 数组,每个 Ability 对应一个相对独立的功能单元 |
requestPermissions |
权限声明数组,敏感权限需声明 reason 说明用途 |
window |
窗口配置(设计宽度等) |
3.3 main_pages.json 路由文件
文件路径:entry/src/main/resources/base/profile/main_pages.json
json
{
"src": [
"pages/Index",
"pages/Detail",
"pages/Settings"
]
}
该文件列出了模块中的所有页面路径,页面跳转时使用 router.pushUrl 需要目标页面在此注册。
四、Ability 概念与 UIAbility 生命周期
4.1 什么是 Ability
Ability 是 HarmonyOS 中应用的基本功能单元。Stage 模型下主要分为两种:
- UIAbility:带有 UI 界面的 Ability,是应用与用户交互的主要载体。一个应用可包含多个 UIAbility。
- ExtensionAbility:无界面的扩展 Ability,提供特定场景的服务(如 FormExtensionAbility、InputMethodExtensionAbility 等)。
本文聚焦 UIAbility。
4.2 UIAbility 生命周期
UIAbility 从创建到销毁经历完整的生命周期,各个阶段都有对应的回调方法。
typescript
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
console.log('onCreate: Ability 被创建');
// 全局初始化操作(仅一次):初始化数据库、SDK 等
}
onDestroy() {
console.log('onDestroy: Ability 被销毁');
// 释放全局资源
}
onWindowStageCreate(windowStage: window.WindowStage) {
console.log('onWindowStageCreate: 窗口舞台创建');
// 加载页面
windowStage.loadContent('pages/Index', (err, data) => {
if (err.code) {
console.error('加载页面失败', JSON.stringify(err));
return;
}
console.log('页面加载成功');
});
}
onWindowStageDestroy() {
console.log('onWindowStageDestroy: 窗口舞台销毁');
// 窗口销毁时处理
}
onForeground() {
console.log('onForeground: Ability 切换到前台');
// 应用从后台回到前台
}
onBackground() {
console.log('onBackground: Ability 切换到后台');
// 应用切换到后台
}
}
4.3 生命周期回调触发时机
| 回调 | 触发时机 | 适用场景 |
|---|---|---|
onCreate |
Ability 首次创建时 | 全局初始化(数据库连接、SDK 初始化) |
onWindowStageCreate |
窗口创建并加载页面时 | 设置主页面 loadContent |
onForeground |
Ability 从后台回到前台 | 恢复任务、刷新数据 |
onBackground |
Ability 从前台切到后台 | 保存状态、暂停任务 |
onWindowStageDestroy |
窗口销毁时 | 窗口资源释放 |
onDestroy |
Ability 被销毁时 | 释放全局资源、反初始化 |
4.4 典型生命周期流程
首次启动:
onCreate → onWindowStageCreate → onForeground
切到后台:
onBackground
从后台返回:
onForeground
退出应用:
onBackground → onWindowStageDestroy → onDestroy
五、启动模式 ------ singleton、multiton、specified
启动模式决定了 UIAbility 在任务栈中的实例化行为。在 module.json5 中通过 launchType 字段配置。
5.1 singleton ------ 单实例模式(默认)
整个系统中只有一个该 Ability 的实例。无论从哪里启动,都会复用已有的实例。
json5
{
"abilities": [{
"name": "EntryAbility",
"launchType": "singleton"
}]
}
行为:
- 首次启动时创建新实例。
- 再次启动时复用已有实例,并触发
onNewWant回调传递新的 Want。 - 适合主页面、全局单一功能页面。
5.2 multiton ------ 多实例模式
每次启动都创建一个新的 Ability 实例,多个实例独立存在于任务栈中。
json5
{
"abilities": [{
"name": "DocAbility",
"launchType": "multiton"
}]
}
行为:
- 每次启动都走完整的
onCreate→onWindowStageCreate→onForeground。 - 不同实例之间相互独立。
- 适合文档编辑、商品详情等需要多任务并行的页面。
5.3 specified ------ 指定实例模式
通过自定义 Key 来决定是创建新实例还是复用已有实例。需要在 Ability 中重写 onAcceptWant 方法。
json5
{
"abilities": [{
"name": "ChatAbility",
"launchType": "specified"
}]
}
自定义 Key 逻辑:
typescript
export default class ChatAbility extends UIAbility {
onAcceptWant(want: Want): string {
// 返回的字符串作为该实例的唯一标识
// 如聊天页中,以联系人 ID 作为 Key,同一联系人的聊天复用同一实例
return want.parameters?.contactId as string ?? '';
}
}
行为:
- 系统根据
onAcceptWant返回的 Key 查找已有实例。 - 若存在相同 Key 的实例则复用,触发
onNewWant。 - 若不存在则创建新实例。
- 适合按特定维度分组的场景(如按联系人、按会话)。
5.4 三种模式对比
| 模式 | 实例数 | 复用行为 | 适用场景 |
|---|---|---|---|
singleton |
全局唯一 | 复用已有,触发 onNewWant |
应用主页、设置页 |
multiton |
每次新建 | 不复用 | 文档、商品详情 |
specified |
按 Key 决定 | 同 Key 复用,不同 Key 新建 | 聊天、邮件会话 |
六、Want 详解与跨 Ability 跳转
Want 是 HarmonyOS 中组件间通信的核心对象,用于启动 Ability、传递参数、匹配目标等。
6.1 Want 的结构
typescript
interface Want {
deviceId?: string; // 目标设备 ID("" 表示本机)
bundleName?: string; // 目标应用包名
abilityName?: string; // 目标 Ability 名称
uri?: string; // 目标 URI(用于隐式匹配)
type?: string; // MIME 类型
action?: string; // 动作
entities?: string[]; // 实体类别
flags?: number; // 标志位
parameters?: Record<string, Object>; // 传递的参数
}
6.2 显式 Want 跳转
明确指定目标应用的 bundleName 和 abilityName。
同一应用内跳转:
typescript
import common from '@ohos.app.ability.common';
let context = getContext(this) as common.UIAbilityContext;
let want: Want = {
deviceId: '', // 空表示本机
bundleName: 'com.example.myapp',
abilityName: 'SecondAbility',
parameters: {
from: 'MainPage',
userId: 123
}
};
context.startAbility(want).then(() => {
console.log('跳转成功');
}).catch((err) => {
console.error('跳转失败', JSON.stringify(err));
});
目标 Ability 接收参数:
typescript
export default class SecondAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
let from = want.parameters?.from as string;
let userId = want.parameters?.userId as number;
console.log(`来自:${from}, userId:${userId}`);
}
}
6.3 隐式 Want 跳转
不指定具体 Ability,通过 action、entities 等由系统匹配。
typescript
let want: Want = {
action: 'ohos.want.action.viewData', // 系统预定义或自定义的 action
entities: ['entity.system.home'],
uri: 'https://www.example.com'
};
context.startAbility(want).then(() => {
// 系统会匹配符合条件的 Ability
}).catch((err) => {
console.error('无匹配的 Ability', JSON.stringify(err));
});
目标 Ability 需要在 module.json5 中声明对应的 skills:
json5
{
"abilities": [{
"name": "BrowserAbility",
"skills": [
{
"actions": ["ohos.want.action.viewData"],
"entities": ["entity.system.home"],
"uris": [
{
"scheme": "https",
"host": "www.example.com"
}
]
}
]
}]
}
6.4 startAbilityForResult 获取返回结果
启动目标 Ability 后,期望其返回结果时使用 startAbilityForResult。
调用方:
typescript
let want: Want = {
bundleName: 'com.example.myapp',
abilityName: 'SelectAbility'
};
context.startAbilityForResult(want).then((result) => {
let selected = result.want?.parameters?.selected;
console.log('返回结果:', selected);
});
目标 Ability 返回结果:
typescript
// 在目标 Ability 中
let resultWant: Want = {
parameters: {
selected: 'result_data'
}
};
context.terminateSelfWithResult(resultWant);
七、综合示例 ------ 多 Ability 跳转与模式测试
以下示例创建两个 Ability:MainAbility(主页,singleton)和 DetailAbility(详情页,multiton),实现跳转、参数传递和结果返回。
MainAbility.ets:
typescript
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
import common from '@ohos.app.ability.common';
export default class MainAbility extends UIAbility {
onCreate(want, launchParam) {
console.log('MainAbility onCreate');
}
onWindowStageCreate(windowStage: window.WindowStage) {
windowStage.loadContent('pages/MainPage', (err) => {
if (err.code) {
console.error('加载页面失败', JSON.stringify(err));
}
});
}
}
// 在 MainPage 组件中触发跳转
// ...
// let context = getContext(this) as common.UIAbilityContext;
// context.startAbility({
// deviceId: '',
// bundleName: 'com.example.myapp',
// abilityName: 'DetailAbility',
// parameters: { itemId: 42 }
// });
module.json5 中配置两个 Ability:
json5
{
"abilities": [
{
"name": "MainAbility",
"srcEntry": "./ets/mainability/MainAbility.ts",
"launchType": "singleton",
"exported": true
},
{
"name": "DetailAbility",
"srcEntry": "./ets/detailability/DetailAbility.ts",
"launchType": "multiton",
"exported": false
}
]
}
通过此配置,主页始终为单实例,每次打开详情页都创建新实例。
八、常见错误与注意事项
8.1 页面未在 main_pages.json 中注册
使用 router.pushUrl({ url: 'pages/NewPage' }) 跳转时,若 NewPage 未在 main_pages.json 的 src 数组中声明,将导致白屏或报错。
8.2 Ability 未在 module.json5 中声明
自定义的 Ability 必须在 abilities 数组中声明,否则无法通过 startAbility 调用。
8.3 启动模式配置不匹配预期行为
- 期望每次打开都是新页面却用了
singleton,导致复用旧实例、丢失新参数。 - 期望复用同一页面却用了
multiton,导致任务栈堆积多个相同页面。 - 使用
specified时忘记重写onAcceptWant,所有启动行为回退为多实例。
8.4 隐式 Want 匹配失败
- 未在目标 Ability 的
skills中声明对应的actions或uris。 scheme、host等不匹配。- 目标 Ability 的
exported为false(隐式跳转要求exported: true)。
8.5 rawfile 路径错误
rawfile 中的文件路径不包含 rawfile 前缀。如文件 rawfile/config.json,读取时使用 resourceManager.getRawFileContent('config.json')。
九、小结
| 概念 | 关键点 |
|---|---|
| 工程目录 | AppScope 全局配置,entry 主模块,resources/base 资源,rawfile 原始文件 |
app.json5 |
应用包名、版本、图标、名称等全局配置 |
module.json5 |
模块配置:Ability 声明、权限、窗口、页面路由 |
main_pages.json |
页面路由注册文件 |
| UIAbility 生命周期 | onCreate → onWindowStageCreate → onForeground / onBackground → onDestroy |
| singleton | 单实例,复用已有 |
| multiton | 多实例,每次新建 |
| specified | 按 Key 决定是否复用,需重写 onAcceptWant |
| Want | 跳转对象:显式(指定 bundleName + abilityName)、隐式(action 匹配)、参数传递 |
掌握工程结构、配置文件与 Stage 模型核心知识后,你将能够从应用全局视角进行设计,合理组织代码模块,实现多 Ability 间的灵活跳转与数据通信,为后续开发复杂项目铺平道路。
觉得文章有帮助?别忘了:
👍 点赞 👍 -- 给我一点鼓励
⭐ 收藏 ⭐ -- 方便以后查看
🔔 关注 🔔 -- 获取更新通知
标签: #HarmonyOS #Stage模型 #UIAbility #Want #启动模式 #工程目录 #配置文件 #学习笔记 #鸿蒙开发