打开一个新工程时,目录只能告诉我们“文件在哪里”。真正开始写代码,还要知道另一件事:用户点了桌面图标之后,系统先找到谁,谁再把第一屏显示出来。
这篇就从这个动作开始。Android 侧看 AndroidManifest.xml 和 MainActivity,HarmonyOS 侧看 module.json5 和 EntryAbility。先把入口跑通,再谈页面跳转、状态管理和业务架构,会更稳。
Android:先从 Manifest 找到启动 Activity
Android 应用不是因为某个类叫 MainActivity 就自动启动它。系统会先看 AndroidManifest.xml,找到带有启动意图的 Activity。
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
这段配置可以拆成三层看:
| 配置 | 作用 | 初学时怎么记 |
|---|---|---|
android:name=".MainActivity" |
指向要启动的 Activity 类 | 系统最终会实例化它 |
MAIN action |
说明这是应用的主入口动作 | “我要启动应用” |
LAUNCHER category |
说明它可以出现在桌面启动器里 | “桌面图标点我” |
进入 MainActivity 后,第一屏通常在 onCreate() 里挂载。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
HomeScreen()
}
}
}
如果是 View/XML 项目,则可能是:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
可以把 Android 第一屏启动链路理解成这样:
这里的关键不是 setContent 或 setContentView 哪个更现代,而是 Activity 站在系统入口和 UI 内容之间:它接住系统启动请求,再把第一屏挂到窗口里。
HarmonyOS:先从 module.json5 找到 EntryAbility
HarmonyOS Stage 模型里,入口声明在 module.json5。以华为代码工坊官方样例 sample_in_harmonyos 的 phone 模块为例,入口相关配置大致长这样:
{
"module": {
"name": "phone",
"type": "entry",
"mainElement": "EntryAbility",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"exported": true,
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["action.system.home"]
}
]
}
]
}
}
这段配置也可以拆开看:
| 配置 | 作用 | 初学时怎么记 |
|---|---|---|
mainElement |
标记模块的主要入口元素 | 这个模块默认先看谁 |
abilities.name |
声明一个 Ability | 类似“这个模块有哪些可被系统调度的能力” |
srcEntry |
指向 Ability 的 ArkTS 文件 | 顺着它找到 EntryAbility.ets |
skills |
描述可被匹配的动作与实体 | 这里承担桌面启动入口的匹配信息 |
对应的 EntryAbility.ets 可以先看骨架:
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 处理启动参数,准备应用级初始化
}
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index');
}
onForeground(): void {
// Ability 进入前台
}
onBackground(): void {
// Ability 进入后台
}
onDestroy(): void {
// Ability 销毁,释放资源
}
}
和 Android 最大的手感差异在 onWindowStageCreate():Ability 创建之后,系统还会创建窗口舞台,页面内容通常在这里通过 windowStage.loadContent(...) 加载。
代码工坊样例里,phone 模块不是直接进入首页,而是先加载 page/SplashPage,再进入主页面。这里的 SplashPage 可以理解为启动过渡页:它通常用于展示启动画面、承接初始化结果或做路由分发。它不是一个新的系统概念,只是业务里常见的“启动后先看到的过渡页面”。
Want 和 Intent:入口参数放在哪里
在 Android 里,Activity 启动时经常从 intent 里取参数:
class DetailActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val articleId = intent.getStringExtra("article_id")
}
}
HarmonyOS 的 UIAbility.onCreate() 会收到 want:
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
const articleId = want.parameters?.['article_id'] as string | undefined;
}
可以先用这个小表建立直觉:
| 问题 | Android Intent | HarmonyOS Want |
|---|---|---|
| 它是什么 | 启动组件和传递参数的信息载体 | 启动 Ability 和传递参数的信息载体 |
| 常见入口 | activity.intent、onNewIntent() |
onCreate(want, launchParam)、onNewWant() |
| 参数位置 | extras |
parameters |
| 显式目标 | component / package / class | bundleName / abilityName |
| 迁移时要小心 | 不要只把它当成 key/value 包,Intent 还涉及隐式匹配规则 | 不要只把它当成 extras,Want 还承载 Ability 与设备等语境 |
这篇不展开 Intent 和 Want 的全部差异,先抓入口层最常见的一点:启动参数可以在入口生命周期里读取,但不要让入口生命周期承担复杂业务。
更稳的做法是:入口读取 Intent / Want 后,把它转成页面路由或业务事件,再交给导航层、页面状态或业务层处理。
生命周期:不要硬凑方法名,要看阶段
Activity 和 UIAbility 都有生命周期,但不能把方法名一一硬套。先把两张官方生命周期图放在一起看,会更容易看到差异:Android 的 Activity 生命周期更围绕“可见/可交互/停止/销毁”展开,HarmonyOS 的 UIAbility 还要额外关注 WindowStage 这一层。
Android Activity 生命周期

HarmonyOS UIAbility 生命周期

按阶段对齐,会比按方法名对齐更清楚:
| 阶段 | Android 里常看哪里 | HarmonyOS 里常看哪里 | 开发时关注什么 |
|---|---|---|---|
| 创建入口 | Activity.onCreate() |
UIAbility.onCreate() |
读取启动参数、准备轻量初始化 |
| 加载第一屏 | setContent / setContentView |
windowStage.loadContent(...) |
把页面挂到窗口,不把业务塞满 |
| 进入前台 | onResume() |
onForeground() |
恢复交互、刷新轻量状态 |
| 退到后台 | onPause() / onStop() |
onBackground() |
暂停资源、保存必要状态 |
| 销毁释放 | onDestroy() |
onWindowStageDestroy() / onDestroy() |
释放监听、订阅、长连接等资源 |
举个很小的迁移例子:如果 Android 里你在 onResume() 做页面可见后的轻量刷新,到 HarmonyOS 里不要直接找一个“同名方法”,而是先问:这个刷新是和 Ability 回到前台有关,还是和某个 ArkUI 页面显示有关?前者可能放在 onForeground(),后者更适合放在页面自己的生命周期或状态刷新逻辑里。
第一屏页面:@Entry 不是 UIAbility
HarmonyOS 页面里经常能看到这样的写法:
@Entry
@Component
struct Index {
build() {
Column() {
Text('CodeHubble')
Button('开始学习')
.onClick(() => {
// TODO
})
}
}
}
@Entry 标记的是页面入口组件,不是应用入口 Ability。简单说:
EntryAbility是系统调度的 Ability,负责接住应用启动、创建窗口、加载页面;@Entry组件是某个页面文件里的入口 UI 组件,负责描述这页的界面;- 一个页面文件里通常只应该有一个
@Entry; - 其他可复用 UI 组件可以用
@Component;如果项目使用状态管理 V2,再按规范使用@ComponentV2组织自定义组件。
也就是说,初学时可以按这个顺序找代码:
这个顺序很重要。不要只在 pages/Index.ets 里找入口,否则会漏掉系统真正调度 Ability 的那一层。
初始化应该放哪:把模糊的“入口附近”拆开
入口文件很容易越写越胖。下面这张表可以作为迁移时的第一版整理规则:
| 事情 | Android 更明确的位置 | HarmonyOS 更明确的位置 | 建议 |
|---|---|---|---|
| 读取启动参数 | Activity.onCreate() 读取 intent;新 Intent 看 onNewIntent() |
UIAbility.onCreate(want, launchParam);新 Want 看 onNewWant() |
入口读取,尽快转给路由或业务事件 |
| 挂第一屏 | Activity.onCreate() 里 setContent / setContentView |
UIAbility.onWindowStageCreate() 里 windowStage.loadContent(...) |
入口负责挂载,不负责首页业务细节 |
| 应用级初始化 | 自定义 Application.onCreate(),或 DI 框架初始化入口 |
ApplicationContext、Ability 启动阶段、必要的全局状态初始化 |
只放轻量、确定、必要的初始化 |
| 页面数据加载 | ViewModel / Repository / use case | 页面状态容器、业务层、服务类 | 不要阻塞入口生命周期 |
| 订阅与监听 | onStart/onResume 注册,onPause/onStop/onDestroy 释放 |
onForeground/onBackground/onWindowStageDestroy/onDestroy 配对处理 |
注册和释放要成对出现 |
| 深链/通知/卡片拉起分发 | Intent 解析后交给导航层 | Want 解析后交给路由或页面层 | 入口只做分发,不做复杂业务判断 |
如果只记一句话:入口层负责接系统、挂窗口、分发参数;业务层负责决定数据怎么来、页面怎么变。
最后再汇总一次
前面逐层拆完后,再看总表会更有感觉:
| 关注点 | Android 常见写法 | HarmonyOS 常见写法 | 迁移时先怎么理解 |
|---|---|---|---|
| 应用入口组件 | MainActivity / 其他启动 Activity |
EntryAbility / 其他 UIAbility |
都是系统调度的组件,不是普通函数入口 |
| 入口声明位置 | AndroidManifest.xml 的 activity + intent-filter |
module.json5 的 abilities + skills |
先看配置,再看代码 |
| 第一屏加载 | setContentView(...) 或 Compose setContent { ... } |
windowStage.loadContent(...) |
Android 常在 Activity 里挂 UI,HarmonyOS 在窗口舞台创建后加载页面 |
| 启动参数 | Intent / extras |
Want / parameters |
先读参数,再分发给路由或业务层 |
| 页面入口 | Compose @Composable 或 XML layout |
@Entry + @Component / @ComponentV2 |
UI 页面入口和系统 Ability 入口是两层 |
| 生命周期 | Activity 生命周期 | UIAbility + WindowStage 生命周期 | 按“创建、挂屏、前台、后台、销毁”理解,不要硬凑方法名 |
这条启动链路理清之后,后面的 Intent vs Want、页面路由、状态管理才有落点:它们不是孤立 API,而是沿着入口、页面、生命周期一层层长出来的。
参考素材
- Android Activity 官方介绍:https://developer.android.com/guide/components/activities/intro-activities
- Android Activity lifecycle 官方指南:https://developer.android.com/guide/components/activities/activity-lifecycle
- OpenHarmony UIAbility lifecycle 官方文档:https://gitee.com/openharmony/docs/blob/master/en/application-dev/application-models/uiability-lifecycle.md
- HarmonyOS UIAbility 组件概述:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/uiability-overview
- HarmonyOS UIAbility 生命周期:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/uiability-lifecycle
- 华为代码工坊官方样例:<https://gitcode.com/HarmonyOS_Samples/sample_in_harmonyos>
// Kai@CodeHubble
// 观测坐标:Android-HarmonyOS/2026-05-08