先抛结论(用 Android 做对照,但主角始终是 Stage 模型本身):
1) Stage 模型的主线不是“回调名”,而是“舞台对象”:AbilityStage 管组件、WindowStage 管窗口、UIContext 是 UI 侧的上下文入口。 2) UIAbility 可以类比 Activity,但不要当成同名替换:真正要对齐的是“容器 + 栈 + 窗口/上下文”的组织方式。 3) 学习顺序更像一章小书:先理解对象分层,再理解生命周期的“阶段”,最后再落到路由(Want/skills)与启动模式(launchType)。
为了让你一眼看清楚差异,我先把两边的“对象分层”放在一张图里(后面所有细节都围绕这张图展开)。
先把 Stage 模型讲清楚:它到底在解决什么问题?
很多对比文章会从“名字对照表”开始:
- Activity ↔ UIAbility
- Intent ↔ Want
- intent-filter ↔ skills
这些对照可以当作索引,但它解释不了 Stage 模型的关键差异:Stage 把“组件管理”和“窗口管理”显式分层了。 如果你不先把这条主线抓住,后面看生命周期、路由、上下文都会觉得像“记名词”。
下面我会先按 Stage 模型自己的分层把它讲清楚,再用 Android 的对象边界做对照,帮助你把熟悉的经验迁移到正确的位置。
1) 为什么我按 Activity 的写法迁 UIAbility,状态就乱了? 2) 为什么路由规则看起来都配了,但隐式拉起总是“有时行,有时不行”? 3) 为什么我拿到一个 context 以后,窗口/资源/权限的访问方式完全不一样?
根因通常不是 API,而是你对“运行时边界”的默认假设不同。
接下来我用同一套“分层语言”分别把 Android 与 Stage 模型讲一遍,然后给出一份可执行的映射与迁移 checklist。
Android 运行时(对照用):你习惯的边界大概长这样
Android 开发者对 UI 的运行时心智,通常会落在这几个“稳定抓手”上:
- 进程/应用:Application 初始化、全局单例、进程存活与回收
- 任务栈/返回链路:Task / Back Stack 决定返回键与“回到哪一屏”
- 容器(Activity):一屏一个 Activity 或单 Activity 多页面(Fragment/Compose Nav)
- 窗口/渲染:Window / View / Compose 层做 UI 布局与绘制
- 路由契约:
Intent+intent-filter(声明 + 请求,系统负责匹配与分发)
你在 Android 里遇到“返回键不对/页面实例不对/被重复创建”,一般会先从 launchMode、Task 行为、Manifest 声明去定位;遇到“跳转到错的组件”,会回到 intent-filter 的 action/category/data 规则去看匹配。
这套排障路径之所以稳定,是因为 Android 的对象边界相对固定:容器/栈/窗口在大多数应用里都很好对齐。
Stage 模型:AbilityStage / WindowStage / UIContext 这条主线
Stage 模型这个名字本身就暗示了它的主线:用“舞台对象”把职责拆开。
- AbilityStage:组件管理的“舞台”
- WindowStage:窗口管理的“舞台”
- UIAbility:作为能力入口的 UI 容器(在 Stage 模型中是主角之一)
- UIContext:ArkUI 侧的 UI 上下文入口(你在组件里经常通过
getUIContext()拿到它)
如果你只把 UIAbility 看成 Activity,会天然忽略两个很重要的事实(也就是 Stage 模型“分层”的落点):
1) UIAbility 的生命周期里有一段明确的“窗口舞台创建”阶段(比如 onWindowStageCreate)。 2) 你在 ArkUI 组件里拿到的往往不是“AbilityContext”,而是 UIContext,再通过 getHostContext() 回到宿主的 UIAbilityContext。
这不是文字游戏,它会直接影响你的代码落点:窗口相关的事,应该放在 WindowStage 创建阶段做;UI 组件拿 window/资源时,要从 UIContext 回到宿主上下文。
举个很典型、也很“工程味”的例子:在 ArkUI 组件内拿到主窗口(你一眼就能看出它的层级关系):
import { common } from '@kit.AbilityKit';
const mainWindow = (this.getUIContext().getHostContext() as common.UIAbilityContext)
.windowStage
.getMainWindowSync();
你看到了:UIContext → HostContext(UIAbilityContext) → windowStage → mainWindow。这条链路把“UI 组件”和“窗口舞台”连接了起来。
对照映射:把对象对齐到同一张“边界地图”上
下面这张表我刻意不按“名字相似度”排,而是按“迁移时你会写错的位置”排:你要先知道它在运行时的哪一层,才知道应该把代码放在哪。
| 你在设计时关心的边界 | Android(你熟悉的对象) | Stage 模型(对应对象) | 迁移落点 |
|---|---|---|---|
| 应用/进程级初始化 | Application |
HAP/应用级对象(入口配置在 app.json5 / module.json5) |
全局初始化 vs Ability 内初始化分清 |
| 组件管理边界 | Manifest 声明 + AMS/系统调度 | AbilityStage |
“组件管理”不要混进窗口逻辑 |
| UI 容器边界 | Activity |
UIAbility |
把它当“能力入口 + UI 容器”,别当纯回调集合 |
| 窗口管理边界 | Window / WindowManager |
WindowStage(及主窗口) |
窗口相关逻辑在 WindowStage 阶段收口 |
| UI 上下文入口 | Context(Application/Activity) |
UIContext(再 getHostContext()) |
UI 组件里先 UIContext,再回宿主上下文 |
| 路由契约(声明 + 请求) | intent-filter + Intent |
skills + Want |
先做“最小规则可验证”,再扩展 |
你不需要背表。你只要记住一条:Stage 模型里,窗口相关逻辑优先挂在 WindowStage 这条线上;UI 侧上下文优先从 UIContext 走回宿主上下文。 这条记住,后面的生命周期与路由会自然落到正确位置。
启动模式:Stage 的 `launchType` 应该怎么理解?
Android 里,Activity 是否“复用实例”通常用 launchMode(以及任务栈相关 flag)去控制。这里我只把它当作对照背景。
在 Stage 模型里,UIAbility 的启动模式更靠近配置侧:你会在 module.json5 里看到 launchType 这个字段:
multiton:多实例(每次启动创建新实例)singleton:单实例(仅第一次启动创建新实例)specified:指定实例(运行时由开发者决定是否创建新实例)standard:multiton的曾用名(等价)
学习/设计 Stage 模型时也不要用“单例/多例”的中文直觉直接拍脑袋,而是回到这条判断链:
1) 这个 UIAbility 是不是对外入口?(对外入口通常更倾向稳定的实例策略) 2) 这个能力是否允许并行存在多个任务实例?(比如文档/编辑器这类) 3) 你的返回链路是“能力级”还是“页面级”?(能力级多实例会直接影响返回体验)
你只要把启动模式的判断放回到“栈模型 + 入口语义”,就不容易写出“看起来能跑,但返回键永远不对”的版本。
生命周期:先抓住“窗口舞台”阶段,再去看回调名
我不准备在这里做完整的回调对照表(那会把读者带回“翻译器”思路)。但有两条实践建议是值得记住的:
1) Android 里你常把 UI 初始化放在 onCreate,Stage 模型里要特别重视 WindowStage 创建阶段。 2) 如果你在 UI 组件里需要 window/资源/权限相关对象,优先从 UIContext → HostContext 回到 UIAbilityContext。
如果你需要一个“视觉锚点”,我们系列前面已经用过两张生命周期图(你可以把它当成排障时的快速参考):
- Android Activity lifecycle
- HarmonyOS UIAbility lifecycle


落地 checklist:把“对象分层”变成可执行步骤
最后给一份我自己在 Stage 项目里会用的 checklist。它比“回调对照表”更能帮你把概念落到代码位置上:
1) 先画对象分层:这段逻辑属于“组件管理/窗口管理/UI 渲染/路由契约”的哪一层? 2) 再定容器粒度:这是 Page 级(页面内切换)还是 UIAbility 级(能力入口)? 3) 启动模式先定再写:先把 launchType 选对,否则后面所有返回/复用都是补丁。 4) 窗口相关逻辑收口到 WindowStage:别分散在到处都能触发的回调里。 5) 路由契约按“声明 + 请求”写:skills 是声明,Want 是请求;先最小集合可验证,再扩展。 6) UI 组件拿能力对象走 UIContext 链路:getUIContext() → getHostContext() →(必要时)windowStage / resourceManager 等。
当你按这 6 步走完,再去做 Intent/Want、intent-filter/skills 的字段对照,你会发现对照表反而更容易写对——因为你知道每个字段应该服务哪个“边界”。
参考素材(精选)
- Android 官方:Intents and Intent Filters
- HarmonyOS 官方:ArkUI Stage模型简介
- OpenHarmony 文档:Want overview / Want API reference
https://developer.android.com/guide/components/intents-filters
https://developer.huawei.com/consumer/cn/arkui/arkui-stage/
https://gitee.com/openharmony/docs/blob/master/en/application-dev/application-models/want-overview.md https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/apis/js-apis-application-want.md
// Kai@CodeHubble
// 观测坐标:Android-HarmonyOS/2026-05-12