你是不是也在想——“鸿蒙这么火,我能不能学会?”
答案是:当然可以!
这个专栏专为零基础小白设计,不需要编程基础,也不需要懂原理、背术语。我们会用最通俗易懂的语言、最贴近生活的案例,手把手带你从安装开发工具开始,一步步学会开发自己的鸿蒙应用。
不管你是学生、上班族、打算转行,还是单纯对技术感兴趣,只要你愿意花一点时间,就能在这里搞懂鸿蒙开发,并做出属于自己的App!
📌 关注本专栏《零基础学鸿蒙开发》,一起变强!
每一节内容我都会持续更新,配图+代码+解释全都有,欢迎点个关注,不走丢,我是小白酷爱学习,我们一起上路 🚀

前言

打开一个 Stage 模型的 HarmonyOS 工程,经常能在 module.json5 里看到不止一个 srcEntry。一个可能直接写在 module 下,另一个又出现在 abilities 数组里。

名字完全一样,很容易产生一个疑问:它们到底是不是同一个“入口”?

这篇文章不扩展到整个 module.json5,也不讨论页面路由、mainElement 或其他配置,只盯住一个字段:srcEntry。我们用一个很小的实验,把 AbilityStage 文件换个位置,再修改 srcEntry,最后通过日志观察系统实际加载的是谁。

先给出本文最关键的结论:

当 srcEntry 直接配置在 module 节点下时,它指定的是该 Module 的 AbilityStage 源文件路径,也就是 HAP 被加载时对应的模块级代码入口;它不是 ArkUI 页面入口,也不是 EntryAbility 的入口。

华为官方对 AbilityStage 的定义很明确:AbilityStage 是 Module 级别的组件容器,一个 AbilityStage 实例对应一个 Module;HAP 中的代码首次被加载到进程时,系统会创建 AbilityStage 实例。官方开发指南同时给出了在 module.json5 的 module 节点中配置 srcEntry 指向 AbilityStage 文件的方式。

一、先从 module.json5 看这个字段

一个典型的 Stage 模型 entry 模块里,可能存在类似配置:

{
  "module": {
    "name": "entry",
    "type": "entry",
    "srcEntry": "./ets/stage/EntryStage.ets",

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

这里出现了两个 srcEntry。

真正需要关注的是字段所在的层级。

配置位置指向对象
module.srcEntryModule 对应的 AbilityStage 源文件
module.abilities[].srcEntry对应 UIAbility 的源文件

官方关于独立进程的开发指导里恰好把这两种配置放在了同一个示例中:module 下的 srcEntry 指向 MyAbilityStage.ets,而 abilities 中的 srcEntry 指向具体 UIAbility 文件。官方还明确说明,配置文件标签下的 srcEntry 用于指定 AbilityStage 源文件路径。

所以判断 srcEntry 是什么入口,不能只搜索字段名:

srcEntry

而要先看它在哪里:

module.srcEntry

还是:

module.abilities[x].srcEntry

这也是这个字段最容易产生理解偏差的地方。

二、为什么 module.srcEntry 会指向 AbilityStage

Stage 模型里,AbilityStage 位于 UIAbility 之前的模块级生命周期位置。

华为官方资料说明,每个 Entry 类型或 Feature 类型的 HAP 在运行期都有 AbilityStage 实例;当 HAP 中的代码第一次被加载进进程时,系统会先创建 AbilityStage。

AbilityStage 开发指南进一步说明:在 Module 的第一个 UIAbility 实例加载之前,会创建 AbilityStage 实例,并触发它的 onCreate()。因此这里适合执行模块初始化一类操作。

于是配置和代码之间就形成了下面这条关系:

entry/src/main/module.json5
        │
        └── module.srcEntry
                 │
                 ▼
entry/src/main/ets/.../XXXStage.ets
                 │
                 └── extends AbilityStage
                          │
                          └── onCreate()

这比把 srcEntry 简单理解成“应用启动文件”准确得多。

它描述的是当前 Module 的 AbilityStage 代码入口。

而页面最终显示什么,并不是这个字段负责的事情。

三、HarmonyOS 7 下需要确认什么

HarmonyOS 7.0 对应的 API 版本已经调整为 26.0.0。华为在 26.0.0 升级适配指导中明确说明,HarmonyOS 7.0 对应 API 26.0.0,并建议升级开发套件、评估 API 变化后进行适配。

本文使用的是 Stage 模型下已经存在多代版本的 AbilityStage 能力。当前官方 API 文档仍将 @ohos.app.ability.AbilityStage 列在 Ability Kit 的 Stage 模型接口中;AbilityStageContext 相关接口首批从 API version 9 开始支持,并且只能用于 Stage 模型。

这个最小实验涉及的条件很少:

  • 开发模型:Stage 模型;
  • HarmonyOS 7 开发背景:API 26.0.0;
  • AbilityStage:Ability Kit;
  • ArkTS 导入:AbilityStage 来自 @kit.AbilityKit;
  • 日志示例:hilog 来自 @kit.PerformanceAnalysisKit;
  • 不需要为了这个实验额外声明运行时权限。

这里没有必要引入 ArkUI 页面代码,因为我们验证的是 Module 的加载入口,不是页面渲染。

四、搭一个最小实验:先让 srcEntry 指向旧位置

先假设 AbilityStage 放在:

entry
└── src
    └── main
        ├── module.json5
        └── ets
            └── stage
                └── EntryStage.ets

EntryStage.ets 只做一件事:进入 onCreate() 后写日志。

import { AbilityStage } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

const DOMAIN = 0x0000;
const TAG = 'SrcEntryDemo';

export default class EntryStage extends AbilityStage {
  onCreate(): void {
    hilog.info(
      DOMAIN,
      TAG,
      'AbilityStage loaded from ets/stage/EntryStage.ets'
    );
  }
}

对应的 module.json5:

{
  "module": {
    "name": "entry",
    "type": "entry",
    "srcEntry": "./ets/stage/EntryStage.ets",

    // 其余原有配置保持不变
  }
}

这里的写法与华为 AbilityStage 官方开发指南给出的形式一致:定义一个继承 AbilityStage 的 ArkTS 类,然后在 module.json5 的 module.srcEntry 中填写它的代码路径。官方示例使用的也是 ./ets/.../MyAbilityStage.ets 这种形式。

由于 AbilityStage 的 onCreate() 会在 AbilityStage 实例创建时调用,因此这个回调正好可以用来观察 srcEntry 指向的文件有没有被系统作为 AbilityStage 加载。

五、移动 AbilityStage,再改一次 srcEntry

接下来不改业务页面,也不动 EntryAbility。

只把文件从:

src/main/ets/stage/EntryStage.ets

移动到:

src/main/ets/bootstrap/AppStage.ets

移动后的代码仍然保持最小化,只把类名和日志改得更容易辨认:

import { AbilityStage } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

const DOMAIN = 0x0000;
const TAG = 'SrcEntryDemo';

export default class AppStage extends AbilityStage {
  onCreate(): void {
    hilog.info(
      DOMAIN,
      TAG,
      'AbilityStage loaded from ets/bootstrap/AppStage.ets'
    );
  }
}

然后把 module.json5 改成:

{
  "module": {
    "name": "entry",
    "type": "entry",
    "srcEntry": "./ets/bootstrap/AppStage.ets",

    // 其余原有配置保持不变
  }
}

真正发生变化的只有这一条映射:

修改前
module.srcEntry
    └── ./ets/stage/EntryStage.ets

修改后
module.srcEntry
    └── ./ets/bootstrap/AppStage.ets

结束当前应用进程后重新启动应用,使对应 HAP 再次经历加载过程,然后在日志中按 SrcEntryDemo 过滤。

按照官方定义,新的 AbilityStage 被创建时,应进入当前 srcEntry 所指定类的 onCreate();因此这个实验预期观察到的新日志是:

AbilityStage loaded from ets/bootstrap/AppStage.ets

这里需要特别说明:本文没有实际替读者编译和运行 HarmonyOS 工程,因此不能把上面的内容描述成“实测结果”。它是依据官方对 module.srcEntry 与 AbilityStage onCreate() 生命周期的定义设计出的验证步骤,实际项目应在对应 SDK 和目标设备环境中完成编译、运行和日志确认。

六、这个实验真正验证的是什么

表面上我们只是移动了一个 .ets 文件,实际上是在验证三件事。

第一,AbilityStage 的文件位置不是固定目录约定决定的。

官方示例会创建 myabilitystage 目录,但真正把文件和 Module 关联起来的是 module.srcEntry 配置。官方开发步骤本身也是“创建 AbilityStage 文件 → 在 module.json5 配置 srcEntry”。

第二,文件名也不是模块入口的判断依据。

EntryStage.ets 可以改成 AppStage.ets,目录也可以发生变化。需要同步维护的是 srcEntry 所填写的代码路径,并保证目标文件提供符合 AbilityStage 用法的实现。

第三,不要把 abilities[].srcEntry 一起改了。

我们的实验只是在移动 AbilityStage:

"srcEntry": "./ets/bootstrap/AppStage.ets"

如果工程中还有:

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

后者仍然描述 EntryAbility 对应的 UIAbility 代码路径。官方文档在多个 Ability 配置示例中也是这样使用 abilities[].srcEntry 的。

两个字段同名,但配置对象完全不同。

七、几个很容易出现的理解偏差

1. 把 module.srcEntry 当成首页入口

这是最需要避免的理解。

module.srcEntry 指向 AbilityStage 时,它解决的是 Module/HAP 加载阶段的组件容器入口问题。AbilityStage 是模块级组件容器,不是 ArkUI 页面。

因此看到:

"srcEntry": "./ets/bootstrap/AppStage.ets"

不要继续去 AppStage.ets 里找 build()。

它应该是类似:

export default class AppStage extends AbilityStage {
  onCreate(): void {
  }
}

这样的模块级实现。

2. 只搜 srcEntry,不看 JSON 层级

下面两个配置不能混为一谈:

module.srcEntry
module.abilities[0].srcEntry

遇到配置问题时,先确认层级,往往比直接打开目标 .ets 文件更有效。

3. 移动 AbilityStage 文件,却忘了修改配置

如果代码重构时把:

ets/stage/EntryStage.ets

迁移到了:

ets/bootstrap/AppStage.ets

那么 module.srcEntry 也需要与新的代码路径保持一致。

华为 DevEco Studio 的构建 FAQ 也明确指出,module.json5 出现字段缺失或拼写等配置问题时,会进行 Schema 校验并给出错误位置。因此排查这类问题时,配置文件本身就是重要检查对象。

4. 只切到后台又回来,就期待再次看到 onCreate()

AbilityStage 的 onCreate() 不是页面的 aboutToAppear()。

官方定义是:Module 的第一个 UIAbility 实例加载前创建 AbilityStage,并在 AbilityStage 实例创建时调用 onCreate()。所以验证 srcEntry 时,应围绕 HAP/AbilityStage 的重新加载 来设计实验,而不是把普通页面前后台切换当成 AbilityStage 的重新创建。

八、实际项目里怎么排查 srcEntry

如果怀疑 Module 的 AbilityStage 没有按预期加载,可以沿着一条很短的路径排查:

entry/src/main/module.json5
        ↓
确认 module 节点
        ↓
找到 module.srcEntry
        ↓
核对 ./ets/... 文件路径
        ↓
打开目标 ArkTS 文件
        ↓
确认 default export 的类继承 AbilityStage
        ↓
在 onCreate() 写入可识别日志
        ↓
重新触发 HAP 加载并观察日志

如果看到的 srcEntry 位于 abilities 或 extensionAbilities 内,则先停下来重新确认对象,因为此时它描述的已经不是 module.srcEntry。

这个排查方法的价值就在于它不依赖“默认工程目录应该长什么样”。

配置写到哪里,路径指向哪里,系统就按对应配置寻找相应组件。

开发经验总结

srcEntry 本身并不难,真正容易误解的是它在 module.json5 中可以出现在不同层级。

围绕本文讨论的 module.srcEntry,记住下面这条关系基本就够了:

module.json5
    ↓
module.srcEntry
    ↓
AbilityStage 源文件
    ↓
extends AbilityStage
    ↓
onCreate()

在 HarmonyOS 7 / API 26.0.0 的开发背景下,AbilityStage 仍然属于 Ability Kit 的 Stage 模型能力;这个最小实验不需要额外权限,也不需要 ArkUI 页面参与。HarmonyOS 7.0 与 API 26.0.0 的对应关系来自华为当前升级适配文档。

以后再看到 srcEntry,先不要急着问“它是不是应用入口”。

先看它的完整位置。

如果是:

module.srcEntry

就沿着它找到 AbilityStage。

如果是:

module.abilities[].srcEntry

再去找对应的 UIAbility。

一个字段名,两种上下文。把层级看清楚,srcEntry 指向谁就不再神秘了。

❤️ 如果本文帮到了你…

  • 请点个赞,让我知道你还在坚持阅读技术长文!
  • 请收藏本文,因为你以后一定还会用上!
  • 如果你在学习过程中遇到bug,请留言,我帮你踩坑!
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐