HarmonyOS 系统切换深色模式后谁先收到通知?AbilityStage onConfigurationUpdated 实验【鸿蒙心迹】

你是不是也在想——“鸿蒙这么火,我能不能学会?”
答案是:当然可以!
这个专栏专为零基础小白设计,不需要编程基础,也不需要懂原理、背术语。我们会用最通俗易懂的语言、最贴近生活的案例,手把手带你从安装开发工具开始,一步步学会开发自己的鸿蒙应用。
不管你是学生、上班族、打算转行,还是单纯对技术感兴趣,只要你愿意花一点时间,就能在这里搞懂鸿蒙开发,并做出属于自己的App!
📌 关注本专栏《零基础学鸿蒙开发》,一起变强!
每一节内容我都会持续更新,配图+代码+解释全都有,欢迎点个关注,不走丢,我是小白酷爱学习,我们一起上路 🚀
全文目录:
前言
应用正在前台运行时,如果用户把系统从浅色模式切到深色模式,或者修改了系统语言,这个变化是怎样进入应用进程的?
这个问题如果只盯着 ArkUI 页面,很容易变成“颜色为什么变了”“资源为什么重新匹配了”。这次换一个角度,不讨论 UI 怎么适配,也不做深色资源页面,而是直接观察 AbilityStage 的 onConfigurationUpdated(),再加入 ApplicationContext 的环境变化监听作为对照。
目标只有一个:把“系统配置发生变化 → 应用收到新的 Configuration”这条链路看清楚。
一、先明确:AbilityStage 处在什么位置
在 Stage 模型中,AbilityStage 是 Module 级的组件容器。官方文档说明,一个 AbilityStage 实例对应一个 Module;当应用的 HAP 第一次被加载时,系统会创建 AbilityStage 实例,而且它的创建发生在该 Module 第一个 UIAbility 实例加载之前。
这也是为什么观察系统配置变化时,AbilityStage 很有意思。
它不是某个 ArkUI 页面,也不是某个具体 UIAbility,而是站在 Module 这一层。官方明确列出了 AbilityStage 的几个回调,其中就包括:
onCreate()、onAcceptWant()、onConfigurationUpdated() 和 onMemoryLevel()。
其中 onConfigurationUpdated() 会在全局系统配置发生变化时触发,官方举出的配置变化就包括系统语言、主题等。
所以这次实验不需要先写一个“深色模式页面”,真正需要观察的是:
系统配置改变以后,AbilityStage 收到的 Configuration 到底发生了什么变化。
二、HarmonyOS 7 下先把版本关系核对清楚
截至 2026 年 9 月,华为官方升级文档已经明确:HarmonyOS 7.0 对应 API 26.0.0,并建议开发者使用 26.0.0 开发套件进行升级适配。26.0.0 开始,HarmonyOS API 版本号也改为语义化版本格式。
本文讨论的并不是 HarmonyOS 7 才新增的能力。Configuration、EnvironmentCallback 等相关 Stage 模型接口从 API version 9 已经开始提供;当前官方 API 文档仍然保留这些能力,因此在 HarmonyOS 7 / API 26 环境下可以继续使用。
几个与实验直接相关的信息可以先整理出来:
| 项目 | 本文使用情况 |
|---|---|
| 开发模型 | Stage 模型 |
| Kit | Ability Kit |
| AbilityStage | Module 级组件容器 |
| 核心回调 | onConfigurationUpdated() |
| 配置对象 | Configuration |
| 对照监听 | EnvironmentCallback |
| 注册入口 | ApplicationContext.on('environment', callback) |
| HarmonyOS 7 | API 26.0.0 |
| 额外权限 | 本实验涉及的配置变化监听不需要额外声明运行时权限 |
module.json5 | 需要通过 Module 的 srcEntry 指定自定义 AbilityStage |
Configuration 本身是环境变化信息的接口定义。官方列出的字段包括 language、colorMode,以及方向、屏幕密度、指针设备、字体缩放等环境信息;其中 language 表示应用语言,colorMode 表示颜色模式。
这意味着测试深色/浅色模式和语言变化,正好可以直接观察这两个字段。
三、最小实验怎么设计
这个实验不需要复杂界面。
我们只准备一个 Entry Module、一个 AbilityStage 和一个普通 UIAbility。
AbilityStage 实现 onConfigurationUpdated();UIAbility 中再通过 ApplicationContext.on('environment', EnvironmentCallback) 注册一个环境变化监听。两边收到配置变化后,都把 Configuration 输出到日志。
这样一次系统配置变化就可能出现两条观察路径:
系统配置变化 → AbilityStage.onConfigurationUpdated()
以及:
系统配置变化 → ApplicationContext 环境监听 → EnvironmentCallback.onConfigurationUpdated()
这里有一个很重要的边界。
官方文档分别说明了这两类回调会在系统环境或全局配置变化时触发,但本文查阅到的官方资料没有把二者之间的先后调用顺序定义成开发者可以依赖的接口契约。
因此,标题里的“谁先收到通知”应该被理解成一个可观察实验问题,而不能预先写成“AbilityStage 一定先于 EnvironmentCallback”之类的结论。
即使某次目标设备上的日志长期表现出固定顺序,也不应该据此编写依赖该顺序的业务逻辑。
四、先把 AbilityStage 接进 Module
DevEco Studio 默认工程不一定自动生成自定义 AbilityStage。官方 AbilityStage 开发指南给出的方式是自行创建 AbilityStage 文件,然后通过 module.json5 中 Module 的 srcEntry 指向它。
例如:
{
"module": {
"name": "entry",
"type": "entry",
"srcEntry": "./ets/myabilitystage/MyAbilityStage.ets"
}
}
实际项目中的 module.json5 当然还会有 abilities、pages、设备类型等其他配置。这里为了突出实验,只保留与 AbilityStage 入口有关的部分。
真正容易漏掉的是 srcEntry。
仅仅创建一个继承 AbilityStage 的 ArkTS 文件,并不能代替 Module 入口配置。官方示例同样通过 srcEntry 指定 AbilityStage 源文件。
五、在 AbilityStage 里观察 Configuration
接下来只做一件事:系统配置变化时打印新的 Configuration。
import { AbilityStage, Configuration } from '@kit.AbilityKit';
export default class MyAbilityStage extends AbilityStage {
onCreate(): void {
console.info('[ConfigTrace][AbilityStage] onCreate');
}
onConfigurationUpdated(config: Configuration): void {
console.info(
`[ConfigTrace][AbilityStage] onConfigurationUpdated: ${JSON.stringify(config)}`
);
}
}
这段代码解决的问题非常单纯:不关心页面是否重新渲染,也不修改任何颜色,只观察 AbilityStage 收到的配置对象。
Configuration 从 @kit.AbilityKit 导入。官方 API 参考中,Configuration 用来描述环境变化信息,其中 language、colorMode 都是可选字段。
如果希望日志更聚焦,也可以只读取本文关心的两个字段:
import { AbilityStage, Configuration } from '@kit.AbilityKit';
export default class MyAbilityStage extends AbilityStage {
onConfigurationUpdated(config: Configuration): void {
console.info(
`[ConfigTrace][AbilityStage] language=${config.language}, colorMode=${config.colorMode}`
);
}
}
这里不要把 colorMode 自己定义成 "dark"、"light" 之类的字符串。官方 Configuration.colorMode 的类型是 ConfigurationConstant.ColorMode。
六、再增加一条 ApplicationContext 观察链路
只看 AbilityStage,我们可以证明 Module 层能够接收配置变化,但还回答不了标题里的“谁先收到”。
因此增加第二个观察点。
官方 EnvironmentCallback 文档说明,在调用 ApplicationContext.on('environment', environmentCallback) 注册系统环境变化监听后,系统环境变化会触发 EnvironmentCallback.onConfigurationUpdated(config)。这个回调拿到的同样是变化后的 Configuration。
可以在 EntryAbility 中这样组织:
import {
UIAbility,
EnvironmentCallback,
Configuration
} from '@kit.AbilityKit';
let callbackId: number;
export default class EntryAbility extends UIAbility {
onCreate(): void {
const environmentCallback: EnvironmentCallback = {
onConfigurationUpdated: (config: Configuration): void => {
console.info(
`[ConfigTrace][EnvironmentCallback] ${JSON.stringify(config)}`
);
},
onMemoryLevel: (): void => {
}
};
const applicationContext = this.context.getApplicationContext();
callbackId = applicationContext.on('environment', environmentCallback);
}
onDestroy(): void {
const applicationContext = this.context.getApplicationContext();
applicationContext.off('environment', callbackId);
}
}
这段代码真正需要关注的是注册关系,而不是 UIAbility 本身。
EnvironmentCallback 不是“页面生命周期”。它是通过 ApplicationContext 注册的系统环境变化监听器。当前官方文档还特别说明,onConfigurationUpdated() 回调运行在当前进程主线程,因此不建议在这里执行耗时的 UI 组件释放操作。
另外,既然通过 on('environment', ...) 完成了注册,就应该保存返回的 callbackId,并在不再需要监听时通过对应的 off 接口解除注册。官方 EnvironmentCallback 示例也是按照这一结构组织的。
七、怎么做深色模式实验
代码准备完成以后,不需要在应用里增加“切换深色”按钮。
这个主题观察的是系统配置变化,因此测试动作应该从系统设置发起。
保持应用进程处于可观察状态,然后在系统设置中切换浅色/深色模式,再查看日志中的:
[ConfigTrace][AbilityStage]
以及:
[ConfigTrace][EnvironmentCallback]
重点检查 Configuration.colorMode。
如果应用自身通过 ApplicationContext.setColorMode() 或 UIAbility 级 setColorMode() 固定了颜色模式,那么还需要注意颜色模式的覆盖关系。当前 UIAbilityContext 官方文档明确给出了优先级:
UIAbility 深浅色模式 > ApplicationContext 应用深浅色模式 > 系统深浅色模式。
因此,做“系统切换深浅色”的纯观察实验时,不要一边把应用固定为 DARK,一边又期待所有应用侧行为都完全跟随系统。
本文的重点也不是证明页面一定变成什么颜色,而是检查回调里的 Configuration。
八、语言变化再测一次
第二组实验可以修改系统语言。
AbilityStage 官方指南明确把系统语言列为全局系统配置变化的例子;Configuration 中也定义了 language 字段。
因此这里观察:
config.language
即可。
测试时不要把重点放在某个 Text 是否自动变成英文,因为那会把多语言资源匹配、页面刷新等另外几个问题混进来。
这次只回答三个问题:回调有没有发生、回调中的 language 是否发生变化、两个观察点各自记录到了什么。
九、“谁先收到通知”到底应该怎么判断
如果确实想观察两个回调的顺序,建议不要只凭肉眼看时间戳。
可以增加一个非常简单的进程内序号工具:
export class ConfigTrace {
private static sequence: number = 0;
static next(source: string): void {
ConfigTrace.sequence++;
console.info(
`[ConfigTrace][${ConfigTrace.sequence}][${source}]`
);
}
}
然后分别调用:
ConfigTrace.next('AbilityStage');
和:
ConfigTrace.next('EnvironmentCallback');
这样一次配置变化发生后,日志中的序号更容易阅读。
但实验结果必须正确理解。
假设某台目标设备某次运行中看到:
[ConfigTrace][1][AbilityStage]
[ConfigTrace][2][EnvironmentCallback]
它只能说明这一次观察到的日志顺序如此。
不能进一步写成:
HarmonyOS 保证 AbilityStage 永远先收到配置变化。
原因很简单:官方资料确认了两种回调机制,却没有在本文所依据的文档中定义二者的固定先后顺序。工程代码如果依赖一个没有被 API 契约保证的回调顺序,后续系统版本、运行环境或者实现变化都可能让这个假设失效。
这其实是这个实验最有价值的地方。
“能够观察到顺序”和“官方保证顺序”是两个完全不同的结论。
十、Configuration 不只是深浅色
如果把完整 Configuration 打出来,会发现这个对象承担的职责比“深色模式状态”大得多。
官方 Configuration API 中定义了语言 language、颜色模式 colorMode,以及屏幕方向、屏幕密度、显示设备、指针设备、字体、字体大小缩放、字重缩放等环境信息,其中部分字段是在后续 API 版本中增加的。
AbilityStageContext 本身也提供 config 属性,用于获取环境配置对象;官方示例直接通过 this.context.config.language 获取当前 Module 的语言环境。AbilityStageContext 属于 Stage 模型,从 API version 9 开始提供。
所以可以把两种情况区分开:
需要读取当前配置时,可以从 Context 的配置对象入手。
需要响应后续配置变化时,则应该关注 onConfigurationUpdated() 或系统环境变化监听。
一个是“现在是什么”,另一个是“什么时候变了”。
十一、几个容易理解错的地方
这里最容易出现的第一个偏差,是把 onConfigurationUpdated() 当成 ArkUI 的页面刷新回调。
它不是。
AbilityStage 是 Module 级组件容器,onConfigurationUpdated() 反映的是系统全局配置变化进入这一层后的事件通知。
第二个偏差,是看到 AbilityStage 创建得比 UIAbility 早,就直接推导出:
“配置变化时 AbilityStage 的回调也一定比其他监听先执行。”
前半句有官方依据:AbilityStage 会在 Module 第一个 UIAbility 实例加载前创建。后半句却不是同一个概念。实例创建顺序不能自动推出以后每一种事件的回调顺序。 官方没有给出这样的保证,就不应该把它写成业务前提。
第三个偏差,是在 onConfigurationUpdated() 里塞大量工作。
EnvironmentCallback 的官方说明已经明确提醒,该配置更新回调运行于当前进程主线程,不建议在里面进行耗时的 UI 组件释放。
观察配置、更新轻量状态、触发后续合理处理可以;把它当成一个不受约束的后台任务入口就不合适了。
第四个偏差,是测试深色模式时忘记应用可能已经覆盖系统颜色模式。尤其在 API 18 及以上,UIAbilityContext 还提供了 UIAbility 级 setColorMode(),而官方定义的优先级中 UIAbility 设置高于 ApplicationContext 设置和系统设置。
十二、实际项目中怎么排查
遇到“系统设置已经变了,但应用没有按预期感知”的问题,可以按下面这个顺序检查:
- 确认工程采用 Stage 模型,并检查当前 SDK/API 版本;HarmonyOS 7 对应 API 26.0.0。
- 如果使用 AbilityStage,检查 Module 的
module.json5是否通过srcEntry正确指向 AbilityStage 文件。 - 在
onConfigurationUpdated()中先完整打印Configuration,不要一开始就把问题归到 ArkUI 页面。 - 深浅色问题重点看
colorMode,语言问题重点看language。 - 如果使用
EnvironmentCallback,确认已经通过ApplicationContext.on('environment', ...)完成注册,并管理好 callbackId 与解除注册。 - 深浅色实验还要检查应用或 UIAbility 是否主动设置了颜色模式,避免应用级配置覆盖系统配置。
- 如果两个回调日志存在先后差异,只把它作为当前环境的观察结果,不要把未被官方定义的执行顺序写成业务依赖。
这套排查方式有一个好处:先判断系统配置有没有真正进入应用,再处理资源匹配、状态管理和 UI 更新。这样可以把问题边界缩得很小。
开发经验总结
AbilityStage 的 onConfigurationUpdated() 本身并不复杂,真正需要理解的是它所处的位置。
AbilityStage 是 Module 级容器,系统语言、主题等全局配置发生变化时,可以通过它观察新的 Configuration;如果需要另一条应用级观察路径,则可以通过 ApplicationContext 注册 EnvironmentCallback。
对于 HarmonyOS 7 / API 26 项目,这些已有 Stage 模型能力仍然可以作为理解系统配置传递机制的基础。HarmonyOS 7 对应 API 26.0.0,而本文涉及的核心 Configuration、EnvironmentCallback 能力早于 API 26 已经提供。
这个最小实验最终应该记住四件事:系统配置变化和 UI 页面变化不是同一个概念;Configuration 才是这次观察的核心数据;AbilityStage 与 EnvironmentCallback 都可以成为观察入口;如果官方没有定义两个回调的固定先后顺序,就不要把一次日志里的顺序升级成系统契约。
如果正在排查深浅色或语言切换问题,可以先暂时放下页面代码,只打印一次 Configuration。很多时候,先弄清楚“变化有没有传进应用、传到了哪一层”,比直接修改 UI 更容易找到真正的问题。
❤️ 如果本文帮到了你…
- 请点个赞,让我知道你还在坚持阅读技术长文!
- 请收藏本文,因为你以后一定还会用上!
- 如果你在学习过程中遇到bug,请留言,我帮你踩坑!
更多推荐




所有评论(0)