大家好,我是[晚风依旧似温柔],新人一枚,欢迎大家关注~

前言

刚接触 HarmonyOS Stage 模型时,module.json5 里的这段配置很容易让人产生一个错觉:name 是不是 ArkTS 类名?srcEntry 和页面路由有什么区别?exported 和 export default class 又是不是一回事?

abilities
 ├ name
 ├ srcEntry
 ├ label
 ├ icon
 └ exported

其实把这几个字段放到一条链路里理解,事情会清楚很多:

abilities 负责向系统注册 UIAbility,srcEntry 把这条注册信息连接到 ArkTS 实现文件,而 ArkTS 文件中的 UIAbility 子类才负责生命周期和窗口逻辑。

本文就围绕这条对应关系,搭一个最小的双 UIAbility 示例,把几个最容易混淆的字段拆开。

一、先搞清楚:abilities 不是 ArkTS 类定义

Stage 模型工程中,一个模块可以在 module.json5 的 abilities 数组里声明 UIAbility。华为官方文档中的多个当前示例,都采用下面这种结构:

{
  "module": {
    "name": "entry",
    "type": "entry",
    "mainElement": "EntryAbility",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "icon": "$media:layered_image",
        "label": "$string:EntryAbility_label",
        "exported": true
      }
    ]
  }
}

官方近期文档给出的多 UIAbility 配置同样是在 abilities 中分别填写 name、srcEntry、icon、label 等信息。

这里最重要的认识是:

module.json5 描述“系统里存在什么组件”,ArkTS 文件描述“这个组件运行起来以后做什么”。

所以不能把 abilities 当成 ArkTS 类的另一种写法。它属于应用组件注册信息。

可以先记住下面这张对应表:

配置主要作用和 ArkTS 文件的关系
nameUIAbility 的逻辑名称用于标识、定位目标 Ability
srcEntryUIAbility 对应代码入口路径指向 .ets 实现文件
labelUIAbility 对用户显示的名称资源不决定 ArkTS 类名
iconUIAbility 的图标资源不决定代码入口
exported控制组件对其他应用的可调用性和 ArkTS 的 export 不是一回事

官方资料对 Ability 信息的定义中,也把 name 作为 Ability 名称,把 label 描述为 Ability 对用户显示的名称,把 icon 描述为 Ability 的图标资源。

二、name:它首先是 Ability 的逻辑名称

先看:

"name": "EntryAbility"

这里比较容易理解错。

name 的核心身份是 Ability 的逻辑名称,而不是“ArkTS 类声明语法”。

这个名称会进入组件标识体系。例如使用 Want 显式指定目标 UIAbility 时,会通过 abilityName 指定目标 Ability。华为当前 Want 文档明确说明:abilityName 表示待启动的 Ability 名称,并且需要在一个应用范围内保证唯一。

例如:

let want: Want = {
  bundleName: 'com.example.abilityconfigdemo',
  abilityName: 'SettingsAbility'
};

这里的:

abilityName: 'SettingsAbility'

对应的首先是 module.json5 中注册的:

{
  "name": "SettingsAbility"
}

而不是让运行时去扫描工程,寻找一个恰好叫 SettingsAbility 的 TypeScript/ArkTS 类。

实际工程中,把配置名称、文件名和类名保持一致仍然是非常好的工程习惯:

name
    ↓
SettingsAbility

srcEntry
    ↓
./ets/settingsability/SettingsAbility.ets

ArkTS
    ↓
export default class SettingsAbility extends UIAbility

这样阅读配置时可以直接定位实现代码,也不容易在多 UIAbility 工程中产生混乱。

三、srcEntry:真正把配置和代码连起来的字段

如果说 name 回答的是“这个组件叫什么”,那么:

"srcEntry": "./ets/settingsability/SettingsAbility.ets"

回答的就是:

这个组件对应的代码在哪里。

华为官方当前开发资料在介绍 ExtensionAbility 注册时,对 srcEntry 的解释非常直接:它表示当前 ExtensionAbility 组件所对应的代码路径;当前其他官方配置示例也沿用同样的“配置组件 + srcEntry 指向实现文件”的组织方式。

UIAbility 工程同样可以看到这种结构:

entry
└─ src
   └─ main
      ├─ ets
      │  ├─ entryability
      │  │  └─ EntryAbility.ets
      │  └─ settingsability
      │     └─ SettingsAbility.ets
      └─ module.json5

对应配置:

"abilities": [
  {
    "name": "EntryAbility",
    "srcEntry": "./ets/entryability/EntryAbility.ets"
  },
  {
    "name": "SettingsAbility",
    "srcEntry": "./ets/settingsability/SettingsAbility.ets"
  }
]

这也是为什么新增一个 .ets 文件,并不等于自动新增了一个系统可识别的 UIAbility。

代码文件存在是一件事,在 abilities 中完成组件注册是另一件事。

官方关于多 UIAbility 的实际配置示例,也是先创建对应 UIAbility 实现,再把新的 Ability 添加到模块的 abilities 中。

四、ArkTS 类到底对应在哪里

以入口 UIAbility 为例,当前官方代码普遍使用 Ability Kit:

import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';

export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        console.error(`loadContent failed: ${JSON.stringify(err)}`);
        return;
      }

      console.info('loadContent success');
    });
  }
}

UIAbility 属于 Ability Kit 的 Stage 模型能力。当前官方 API 体系仍将 @ohos.app.ability.UIAbility 列为“带界面的应用组件”,官方示例使用 @kit.AbilityKit 导入 UIAbility。UIAbility 相关 Stage 模型基础接口从 API version 9 起进入当前接口体系。

这里真正需要分清三层:

module.json5
    │
    │  name = EntryAbility
    │  srcEntry = ./ets/entryability/EntryAbility.ets
    ▼
EntryAbility.ets
    │
    │  export default class EntryAbility extends UIAbility
    ▼
onCreate / onWindowStageCreate / onForeground ...

module.json5 不是页面路由表。

EntryAbility.ets 也不是 ArkUI 页面本身。

UIAbility 创建 WindowStage 后,可以再通过:

windowStage.loadContent('pages/Index')

加载具体 ArkUI 页面。

因此还可以再补上一层:

abilities
   ↓
UIAbility
   ↓
WindowStage
   ↓
pages/Index
   ↓
ArkUI 页面

把这几层分开以后,很多目录结构问题就容易定位了。

五、label 和 icon:它们描述的是组件展示信息

再看:

"label": "$string:EntryAbility_label",
"icon": "$media:layered_image"

它们和 srcEntry 完全不是一个维度。

label 对应 Ability 向用户展示的名称信息,icon 对应 Ability 图标资源。官方 AbilityInfo 资料也分别将二者定义为 Ability 对用户显示的名称和 Ability 图标资源。

因此常见配置会使用资源引用:

"label": "$string:EntryAbility_label",
"icon": "$media:layered_image"

而不是把代码文件写到这里。

例如字符串资源可以放在模块的资源目录中:

{
  "string": [
    {
      "name": "EntryAbility_label",
      "value": "Ability 配置示例"
    },
    {
      "name": "SettingsAbility_label",
      "value": "设置"
    }
  ]
}

icon 则引用对应 media 资源。

这里还有一个很实用的排查点:如果配置引用了不存在的 $media 资源,构建阶段会报资源引用错误。华为 DevEco Studio 官方 FAQ 给出的例子就是 icon 引用了不存在的 media 资源后导致编译失败。

所以:

srcEntry 找不到

和:

icon 资源找不到

虽然都写在同一个 Ability 配置对象中,本质却是两类完全不同的问题。

六、exported:千万不要和 export default 混在一起

这是这几个字段里最值得单独拆出来的一个。

配置文件中有:

"exported": false

ArkTS 文件中又有:

export default class SettingsAbility extends UIAbility {
}

两个地方都有 export,但它们根本不是一个概念。

ArkTS:

export default

属于语言模块的导出语义。

而 module.json5:

"exported": true

属于 应用组件暴露配置,决定该 Ability 是否允许被其他应用调用。

华为官方 2026 年的 DevEco Studio FAQ 给出了一个很直观的案例:Stage 模型运行时如果出现 ability visible false deny request,官方给出的检查方法就是将对应 abilities 中的 exported 设置为 true。

所以不能写成这样的推理:

ArkTS 写了 export default
        ↓
其他应用就能启动这个 Ability

这是错误的概念关联。

正确理解应该是:

export default
    ↓
ArkTS 模块层面的默认导出

exported
    ↓
HarmonyOS 应用组件层面的对外暴露配置

两者所在层级完全不同。

对于只打算在应用内部使用的业务 UIAbility,不应该因为看到 ArkTS 中存在 export default,就顺手把 module.json5 的 exported 也理解成同一个开关。

七、搭一个最小的双 UIAbility 示例

现在把关系组合起来。

目标很简单:

EntryAbility
    ↓
首页

SettingsAbility
    ↓
设置页面

module.json5 可以组织成:

{
  "module": {
    "name": "entry",
    "type": "entry",
    "mainElement": "EntryAbility",
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "label": "$string:EntryAbility_label",
        "icon": "$media:layered_image",
        "exported": true,
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "ohos.want.action.home"
            ]
          }
        ]
      },
      {
        "name": "SettingsAbility",
        "srcEntry": "./ets/settingsability/SettingsAbility.ets",
        "label": "$string:SettingsAbility_label",
        "icon": "$media:layered_image",
        "exported": false,
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background"
      }
    ]
  }
}

当前华为官方多 UIAbility 示例同样采用在 abilities 中分别注册两个组件,并为每个组件指定独立 name 与 srcEntry 的方式。

然后建立:

ets/
├─ entryability/
│  └─ EntryAbility.ets
├─ settingsability/
│  └─ SettingsAbility.ets
└─ pages/
   ├─ Index.ets
   └─ Settings.ets

SettingsAbility.ets:

import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';

export default class SettingsAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/Settings', (err) => {
      if (err.code) {
        console.error(`loadContent failed: ${JSON.stringify(err)}`);
        return;
      }

      console.info('Settings page loaded');
    });
  }
}

这个例子真正需要看的并不是页面长什么样,而是下面的链路:

"name": "SettingsAbility"
             │
             ├── 系统侧组件名称
             │
"srcEntry": "./ets/settingsability/SettingsAbility.ets"
             │
             ├── 找到实现文件
             │
export default class SettingsAbility extends UIAbility
             │
             ├── UIAbility 实现
             │
onWindowStageCreate(...)
             │
             └── loadContent('pages/Settings')
                         ↓
                   ArkUI 页面

这就是 abilities 和 ArkTS 文件之间最核心的对应关系。

八、几个特别容易理解错的地方

1. name 不是文件路径

下面两个字段不要互换职责:

"name": "SettingsAbility",
"srcEntry": "./ets/settingsability/SettingsAbility.ets"

前者是组件名称,后者才是代码入口路径。

Want 显式指定目标 Ability 时关注的是 abilityName。

2. srcEntry 不是 ArkUI 页面地址

"srcEntry": "./ets/entryability/EntryAbility.ets"

和:

windowStage.loadContent('pages/Index')

不是同一件事。

前者把 UIAbility 注册信息连接到实现代码;后者是在 WindowStage 中加载页面内容。

3. label 不等于 name

name 是 Ability 名称。

label 是面向用户的展示名称。

所以这两项完全可以承担不同职责:

"name": "SettingsAbility",
"label": "$string:SettingsAbility_label"

不要为了显示中文名称去修改 Ability 的逻辑名称。

4. icon 不是 startWindowIcon

本文重点是:

"icon": "$media:layered_image"

工程里经常还能看到:

"startWindowIcon": "$media:startIcon"

二者不要混为一个字段。DevEco Studio 官方 FAQ 明确把 startWindowIcon 和 startWindowBackground 放在启动界面配置中说明。

5. exported 也不是权限声明

exported 处理的是组件对外暴露问题,它本身不是:

requestPermissions

也不是某个:

ohos.permission.XXX

本文这个最小示例没有为了 name、srcEntry、label、icon 或 exported 额外申请运行时权限。涉及具体跨应用启动能力时,还应继续按照目标组件和调用接口的官方约束检查权限、启动规则以及组件可见性,不能把 exported: true 理解成“绕过权限检查”。

九、版本和 API Level 应该怎么理解

这个主题还有一个容易出现的版本误区:不要给每个 module.json5 字段机械套一个 ArkTS API Level。

name、srcEntry、label、icon、exported 在这里首先属于 Stage 模型的模块配置,而不是五个需要 import 后调用的 ArkTS API。

与本文代码直接相关的运行时部分是 Ability Kit 的 Stage 模型 UIAbility 体系。当前华为官方 API 文档仍提供 @ohos.app.ability.UIAbility、UIAbilityContext 等能力;UIAbilityContext 文档明确说明其首批接口从 API version 9 开始支持,并且仅可在 Stage 模型下使用。当前推荐代码示例使用 @kit.AbilityKit 导入相关能力。

因此迁移本文思路时,更稳妥的检查方式是:

先确认工程是不是 Stage 模型
        ↓
检查当前 SDK 下 module.json5 schema
        ↓
确认 UIAbility / Ability Kit API 版本
        ↓
再检查具体调用接口的设备和权限限制

而不是看到 exported 就试图寻找一个所谓的 exported() ArkTS API。

十、实际项目中怎么排查

遇到“新增 UIAbility 后启动不到”“配置改了但表现不对”之类问题,可以按下面顺序检查:

  1. 先看 abilities 是否真的注册了目标 UIAbility。 只有 .ets 文件不够。
  2. 检查 name。 如果通过 Want 启动,重点核对 abilityName 与注册的 Ability 名称。
  3. 检查 srcEntry。 路径、目录和文件名是否与实际工程一致。
  4. 检查 ArkTS 实现。 对应文件是否按照 UIAbility 组件方式组织,并正确继承 UIAbility。
  5. 再检查页面加载。 UIAbility 已经找到,不代表 loadContent() 指向的 ArkUI 页面就一定正确。
  6. 检查资源引用。 label、icon、startWindowIcon 等引用的 $string、$media 资源是否真实存在。
  7. 最后看对外启动条件。 如果调用涉及其他应用,继续检查 exported、组件启动规则以及具体接口要求。

官方构建 FAQ 也说明,module.json5 字段缺失或拼写错误可能直接触发 Schema validate failed;报错中的 instancePath、missingProperty、propertyNames 可以用来定位具体 Ability 配置。

开发经验总结

这几个配置项真正值得记住的,不是每个字段的中文翻译,而是它们所在的层次:

abilities
    ↓
注册系统组件

name
    ↓
组件逻辑名称

srcEntry
    ↓
组件实现代码入口

export default class ... extends UIAbility
    ↓
ArkTS 中的 UIAbility 实现

loadContent(...)
    ↓
加载 ArkUI 页面

label、icon 补充的是组件展示信息,exported 处理的是组件对其他应用的暴露边界。

尤其要把两组概念彻底分开:

name ≠ label
srcEntry ≠ ArkUI 页面路由
exported ≠ export default
icon ≠ startWindowIcon

理解到这里,再去看一个包含多个 UIAbility 的 module.json5,就不会只看到一大堆配置字段了,而是能直接读出:系统注册了哪些组件、每个组件的代码入口在哪里、哪个组件对外暴露,以及它最终会加载哪一个页面。

如果正在整理一个多 UIAbility 工程,可以专门检查一次:现在工程里每一个 abilities[].name,是否都能沿着 srcEntry → UIAbility 实现 → loadContent 页面 这条链路准确定位。这个检查对于发现“配置层”和“页面层”混在一起的问题很有价值。

如果觉得有帮助,别忘了点个赞+关注支持一下~
喜欢记得关注,别让好内容被埋没~

Logo

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

更多推荐