【句匠|13】HarmonyOS ArkTS 应用启动链路实战:从 EntryAbility 到首屏加载保持窗口与路由稳定

HarmonyOS 应用能显示第一个页面,并不等于启动链路已经可靠。真实工程里更容易出问题的是页面出现之前的几百毫秒:应用级数据是否已经准备好,窗口对象能否正常取得,状态栏与底部手势区高度有没有同步,媒体查询监听是否已注册,首屏加载失败后有没有留下可定位的日志,窗口销毁时监听是否完整释放。
本文基于句匠项目的真实源码 entry/src/main/ets/entryability/EntryAbility.ets 展开,并用 module.json5、main_pages.json、UserDataManager.ets、BreakpointSystem.ets 和 SplashPage.ets 交叉核对入口前后关系。目标不是再写一个“能启动”的示例,而是把 HarmonyOS 5.0 及以上 Stage Model 应用从 EntryAbility 创建到 SplashPage 首屏装载的责任边界讲清楚。
本文唯一源码标识:com.jiaweikang.one18。
源码能够证明的范围包括:浅色模式设置、本地 Preferences 数据恢复、AppStorage 默认值、三档媒体查询注册、主窗口与避让区监听、loadContent('pages/SplashPage')、生命周期日志和监听释放。源码没有实现账号登录前置、远程配置、广告开屏、推送冷启动、深链参数分发、云端初始化或启动任务并发编排,本文不对这些能力作扩展性宣传。
一、先画清启动时序:页面只是最后一棒
Stage Model 下,桌面点击图标之后并不是立刻执行 SplashPage.aboutToAppear()。句匠的实际顺序可以拆成五段:
- 系统依据
module.json5找到EntryAbility; onCreate()完成应用级状态和依赖初始化;onWindowStageCreate()取得主窗口并建立避让区监听;windowStage.loadContent('pages/SplashPage')装载首屏;SplashPage出现后播放动画,并在 2 秒后替换到Index。

这个顺序决定了初始化放在哪里。首屏一出现就要读取的数据,必须在 loadContent 前准备;依赖 Window 的系统区域信息,必须等到 WindowStage 创建后处理;只属于启动页动画的状态,应留在 SplashPage,不能被抬升到应用级存储。
| 动作 | 所属边界 | 句匠源码位置 | 失败后的影响 |
|---|---|---|---|
| 恢复收藏、错题、进度 | 应用数据初始化 | onCreate() | 首屏读取到空值或旧值 |
| 初始化主 Tab 和安全区默认值 | 应用共享状态 | onCreate() | @StorageLink 缺少稳定初值 |
| 注册宽度断点 | 应用布局环境 | onCreate() | 页面无法跟随窗口宽度更新 |
| 获取主窗口、读取避让区 | 窗口生命周期 | onWindowStageCreate() | 顶部或底部内容可能被系统区域遮挡 |
加载 SplashPage | 首屏装载 | onWindowStageCreate() | 白屏或入口不可用 |
| 清理监听 | 销毁生命周期 | onWindowStageDestroy() / onDestroy() | 重复回调或生命周期泄漏 |
启动问题排查时,先判断故障落在哪个阶段,比盯着第一个页面的 build() 更有效。页面完全没有出现,优先看 Ability 和 loadContent;页面出现但布局压住系统栏,再看避让区同步;主页数据不对,才继续检查本地数据恢复和共享状态。
二、module.json5 决定系统如何找到入口
入口代码能被执行,前提是模块声明和类名一致。句匠的 entry/src/main/module.json5 里,mainElement、abilities.name 与 srcEntry 指向同一个入口:
{
"module": {
"name": "entry",
"type": "entry",
"mainElement": "EntryAbility",
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"icon": "$media:app_icon_square",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:app_icon_square",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["ohos.want.action.home"]
}
]
}
]
}
}
这里有三处会直接影响冷启动。
第一,mainElement 和 Ability 名称都为 EntryAbility,系统入口与 ArkTS 类保持一致。第二,srcEntry 是相对模块目录的真实文件路径,入口重命名后必须同步修改。第三,startWindowIcon 和 startWindowBackground 决定 ArkUI 内容加载前的启动窗口视觉,它们不是 SplashPage 的组件内容。
因此,遇到“点击图标后先闪一下不一致的背景”时,不要只改启动页背景,还应核对启动窗口背景资源与 SplashPage 根容器背景。遇到“安装成功但入口拉不起”时,则应先检查 mainElement、name、srcEntry 和 skills,而不是在页面路由里反复试错。
pages 指向 $profile:main_pages,其列表同时包含 pages/SplashPage 和 pages/Index。这让后面的 loadContent('pages/SplashPage') 与 router.replaceUrl({ url: 'pages/Index' }) 都有明确的页面注册依据。
三、onCreate 只做应用级准备,不碰窗口布局
句匠的 EntryAbility 继承 UIAbility。onCreate() 收到 Want 和 LaunchParam,但当前源码没有解析它们,而是集中完成四类应用级初始化:
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
try {
this.context.getApplicationContext()
.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT)
} catch (err) {
hilog.error(
DOMAIN,
'testTag',
'Failed to set colorMode. Cause: %{public}s',
JSON.stringify(err)
)
}
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate')
UserDataManager.init(this.context)
AppStorage.setOrCreate<number>('currentTabIndex', 0)
AppStorage.setOrCreate<number>('favoriteTabIndex', 0)
AppStorage.setOrCreate<number>('topAvoidAreaHeightPx', 0)
AppStorage.setOrCreate<number>('navigationIndicatorHeightPx', 0)
BreakpointSystem.register()
}
颜色模式设置被 try/catch 包围,失败时记录错误但不阻断后续初始化。当前项目明确把应用设置为浅色模式,因此测试时要验证系统处于深色模式时,启动窗口、Splash 和主页仍保持可读,而不能宣称项目已实现跟随系统的深浅色切换。
UserDataManager.init(this.context) 依赖 UIAbilityContext,放在 Ability 边界比放进页面更合适。页面不需要持有宽泛的 Context,也不需要知道 Preferences 的存储名和键名,只消费恢复后的 AppStorage 数据。
四个 setOrCreate 则提供确定的默认值。尤其是两个避让区高度先写为 0,意味着即使后续窗口 API 抛错,页面也不会拿到未初始化值。setOrCreate 只在键不存在时创建,并不是每次启动都强制覆盖已有状态,这一点与直接 set 的语义不同。
这一阶段没有调用窗口 API,是正确的生命周期分层。WindowStage 尚未交给 onWindowStageCreate(),此时不应假定主窗口已经可用。
四、把本地数据恢复放在 loadContent 之前
句匠的首屏和主页会读取收藏、笔记、错题、学习进度、考试记录等状态。UserDataManager.init() 使用同步 Preferences API 恢复这些数据,并写入 AppStorage:
static init(context: common.UIAbilityContext | common.Context): void {
try {
UserDataManager.prefs = preferences.getPreferencesSync(
context,
{ name: UserDataManager.STORE_NAME }
)
const favStr = UserDataManager.prefs
.getSync(UserDataManager.K_FAV, '[]') as string
const wrongStr = UserDataManager.prefs
.getSync(UserDataManager.K_WRONG, '[]') as string
const progressStr = UserDataManager.prefs
.getSync(UserDataManager.K_PROGRESS, '[]') as string
AppStorage.setOrCreate<FavoriteRecord[]>(
'favoriteRecords',
JSON.parse(favStr) as FavoriteRecord[]
)
AppStorage.setOrCreate<WrongRecord[]>(
'wrongRecords',
JSON.parse(wrongStr) as WrongRecord[]
)
AppStorage.setOrCreate<BankProgress[]>(
'bankProgress',
JSON.parse(progressStr) as BankProgress[]
)
} catch (_) {
AppStorage.setOrCreate<FavoriteRecord[]>('favoriteRecords', [])
AppStorage.setOrCreate<WrongRecord[]>('wrongRecords', [])
AppStorage.setOrCreate<BankProgress[]>('bankProgress', [])
}
}
这段实现给启动链路提供了两个保证:正常时首屏装载前已经有本地数据,异常时至少有空数组和默认设置,不会因为 JSON 损坏或 Preferences 读取失败让 Ability 直接退出。
同时也要看到它的真实边界。这里使用同步读取,数据量目前只是轻量 JSON 字符串,适合当前本地学习记录;如果未来记录规模明显增大,继续在 onCreate() 同步解析大量数据会拉长首屏前耗时。那时应测量启动耗时,再决定是否迁移到 RDB、拆分关键数据与延后数据,而不是未经测量就把所有初始化改成异步。
更稳的启动任务分级可以按首屏依赖划分:
| 等级 | 示例 | 是否必须在 loadContent 前完成 |
|---|---|---|
| 必需 | 主 Tab 初值、首屏直接读取的数据 | 是 |
| 布局必需 | 当前断点、安全区初值 | 至少要有默认值 |
| 可延后 | 非首屏统计汇总、低优先级缓存 | 否 |
| 不属于当前源码 | 登录、远程配置、广告请求 | 不应凭空加入 |
启动优化不是简单地“全部异步化”,而是先识别首屏真正依赖什么,再缩短关键路径。
五、BreakpointSystem 在页面出现前建立布局环境
模块声明支持 phone、tablet 和 2in1,源码用 BreakpointSystem.register() 建立三档媒体查询:
static register(): void {
BreakpointSystem.smListener =
mediaquery.matchMediaSync('(width<=600vp)')
BreakpointSystem.mdListener =
mediaquery.matchMediaSync('(600vp<width<=840vp)')
BreakpointSystem.lgListener =
mediaquery.matchMediaSync('(840vp<width)')
BreakpointSystem.smListener.on('change', result => {
if (result.matches) BreakpointSystem.update('sm')
})
BreakpointSystem.mdListener.on('change', result => {
if (result.matches) BreakpointSystem.update('md')
})
BreakpointSystem.lgListener.on('change', result => {
if (result.matches) BreakpointSystem.update('lg')
})
if (BreakpointSystem.smListener.matches) {
BreakpointSystem.update('sm')
} else if (BreakpointSystem.mdListener.matches) {
BreakpointSystem.update('md')
} else {
BreakpointSystem.update('lg')
}
}
注册后立即执行一次初始判断,避免页面第一次构建时只有监听、没有当前值。update() 把结果写入 AppStorage 的 currentBreakpoint,页面再通过 @StorageLink 响应变化。
这项初始化放在 onCreate(),意味着 SplashPage 装载前断点状态已经可用。当前 Splash 的布局是简单居中结构,没有直接消费断点;真正使用该状态的是后续 Index。不过入口提前建立环境,可以避免进入主页时才注册监听造成首帧布局跳变。
媒体查询监听也是资源,因此 onDestroy() 必须调用 BreakpointSystem.unregister()。如果只注册不释放,在 Ability 重建、窗口反复创建或测试多次进入退出时,会增加重复回调风险。
需要谨慎描述的是:存在 sm/md/lg 三档断点不等于当前每个断点都呈现不同导航。文章能确认的是断点系统已注册并同步状态,具体布局分支仍应以页面源码为准。
六、onWindowStageCreate 才开始操作主窗口
当系统创建窗口舞台后,onWindowStageCreate(windowStage) 获得合法的窗口入口。句匠源码使用同步 API取得主窗口,然后立即读取避让区:
onWindowStageCreate(windowStage: window.WindowStage): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate')
try {
this.mainWindow = windowStage.getMainWindowSync()
this.updateNavigationIndicatorHeight()
this.avoidAreaCallback = (data: window.AvoidAreaOptions) => {
if (data.type === window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR ||
data.type === window.AvoidAreaType.TYPE_SYSTEM) {
this.updateNavigationIndicatorHeight()
}
}
this.mainWindow.on('avoidAreaChange', this.avoidAreaCallback)
} catch (err) {
AppStorage.setOrCreate<number>('topAvoidAreaHeightPx', 0)
AppStorage.setOrCreate<number>('navigationIndicatorHeightPx', 0)
hilog.warn(
DOMAIN,
'testTag',
'Failed to observe avoid area. Cause: %{public}s',
JSON.stringify(err)
)
}
windowStage.loadContent('pages/SplashPage', (err) => {
if (err.code) {
hilog.error(
DOMAIN,
'testTag',
'Failed to load the content. Cause: %{public}s',
JSON.stringify(err)
)
return
}
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.')
})
}
这里的真实 API 是 getMainWindowSync(),不是异步 getMainWindow()。技术文章和实际代码必须一致,因为两种写法的错误处理和回调时序不同。
窗口初始化被一个独立的 try/catch 包住。即使主窗口或监听建立失败,代码仍会继续执行 loadContent,首屏不必因为安全区信息读取失败而完全不可用。代价是安全区值回退到 0,因此真实设备测试还要观察是否出现内容贴边或被遮挡,并结合 warn 日志定位。

这种处理体现了“核心路径与增强信息分级”:首屏可加载属于核心路径,避让区精确值属于布局增强;增强失败可以降级,核心路径失败必须明确记录。
七、避让区回调只响应相关类型
系统可能在旋转、窗口尺寸改变、导航方式变化时触发 avoidAreaChange。句匠没有对所有变化无条件刷新,而是检查事件类型:
this.avoidAreaCallback = (data: window.AvoidAreaOptions) => {
if (data.type === window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR ||
data.type === window.AvoidAreaType.TYPE_SYSTEM) {
this.updateNavigationIndicatorHeight()
}
}
updateNavigationIndicatorHeight() 同时读取顶部系统区域和底部导航区域:
private updateNavigationIndicatorHeight(): void {
if (!this.mainWindow) {
AppStorage.setOrCreate<number>('topAvoidAreaHeightPx', 0)
AppStorage.setOrCreate<number>('navigationIndicatorHeightPx', 0)
return
}
try {
const navigationArea = this.mainWindow.getWindowAvoidArea(
window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR
)
const systemArea = this.mainWindow.getWindowAvoidArea(
window.AvoidAreaType.TYPE_SYSTEM
)
const topHeight =
systemArea.visible ? systemArea.topRect.height : 0
const navigationHeight =
navigationArea.visible ? navigationArea.bottomRect.height : 0
const systemHeight =
systemArea.visible ? systemArea.bottomRect.height : 0
AppStorage.setOrCreate<number>('topAvoidAreaHeightPx', topHeight)
AppStorage.setOrCreate<number>(
'navigationIndicatorHeightPx',
Math.max(navigationHeight, systemHeight)
)
} catch (err) {
AppStorage.setOrCreate<number>('topAvoidAreaHeightPx', 0)
AppStorage.setOrCreate<number>('navigationIndicatorHeightPx', 0)
hilog.warn(DOMAIN, 'testTag', 'Failed to update avoid area.')
}
}
几个细节容易被忽略。
其一,读取的是 px,入口层没有擅自按固定密度转换。页面持有 UIContext,在实际布局处使用 px2vp() 更合理。其二,底部高度取导航指示条和系统区域的最大值,避免只看一种类型时漏掉真实设备上的更大遮挡区域。其三,只有 visible 为真才使用矩形高度,防止不可见区域仍参与布局。
页面层最终通过 @StorageLink('topAvoidAreaHeightPx') 和 @StorageLink('navigationIndicatorHeightPx') 消费结果。这样窗口 API 留在 Ability,ArkUI 页面只处理数值与布局,不必到处传递 Window。
八、loadContent 的回调必须区分成功与失败
入口最后调用:
windowStage.loadContent('pages/SplashPage', (err) => {
if (err.code) {
hilog.error(
DOMAIN,
'testTag',
'Failed to load the content. Cause: %{public}s',
JSON.stringify(err)
)
return
}
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.')
})
pages/SplashPage 必须与 main_pages.json 中的路径一致。大小写、目录或重命名不一致,都可能让 loadContent 失败。回调中先判断 err.code 并立即返回,避免失败后仍打印成功日志。
启动白屏排查可以沿着以下顺序缩小范围:
- 检查
module.json5是否正确声明EntryAbility; - 检查
main_pages.json是否包含pages/SplashPage; - 检查
SplashPage.ets是否带有可作为页面入口的@Entry; - 过滤
testTag日志,确认是否到达onWindowStageCreate; - 查看
Failed to load the content的具体错误; - 若加载成功,再检查 Splash 根容器背景、透明度和动画状态。
句匠的 Splash 初始透明度为 0,并在 aboutToAppear() 中通过 600ms 动画变为 1。因此“短时间看不到内容”还需要区分是页面加载失败,还是页面已加载但仍处于透明动画起点。日志和生命周期断点能帮助两者分离。
九、首屏之后的路由稳定性靠 replaceUrl 与定时器清理
虽然本文主角是 EntryAbility,但首屏是否正确离开决定了启动链路能否闭环。句匠的 SplashPage 在出现时建立动画和延迟跳转:
aboutToAppear(): void {
animateTo({ duration: 600, curve: Curve.EaseOut }, () => {
this.opacity_ = 1
this.scale_ = 1
})
this.timerId = setTimeout(() => {
router.replaceUrl({ url: 'pages/Index' })
}, 2000)
}
aboutToDisappear(): void {
if (this.timerId !== -1) {
clearTimeout(this.timerId)
}
}
replaceUrl 用主页替换当前启动页,用户从主页执行系统返回时不会重新看到 Splash。timerId 在页面消失时被清理,避免页面提前退出后延迟任务继续发起路由。
这段代码也限定了文章结论:当前跳转条件只是固定 2 秒,不包含登录判断、协议确认、远程开关或首次启动引导。若未来增加这些分支,应把“决定下一站”的规则提取为可测试的启动路由策略,同时确保同一次启动只能提交一次导航,避免定时器和异步任务同时跳转。
十、onWindowStageDestroy 与 onDestroy 形成两层清理
句匠在窗口舞台销毁和 Ability 销毁时都尝试注销避让区监听:
onWindowStageDestroy(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy')
if (this.mainWindow && this.avoidAreaCallback) {
this.mainWindow.off('avoidAreaChange', this.avoidAreaCallback)
}
this.mainWindow = undefined
this.avoidAreaCallback = undefined
}
onDestroy(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy')
if (this.mainWindow && this.avoidAreaCallback) {
this.mainWindow.off('avoidAreaChange', this.avoidAreaCallback)
}
BreakpointSystem.unregister()
}
回调保存为字段,而不是注册时写一个无法引用的匿名函数,这让 off 可以传入同一个函数对象。onWindowStageDestroy() 清理窗口引用,onDestroy() 再释放应用级断点监听,职责与创建阶段对应。
| 创建动作 | 清理动作 | 对称性 |
|---|---|---|
BreakpointSystem.register() | BreakpointSystem.unregister() | Ability 级 |
mainWindow.on('avoidAreaChange', callback) | mainWindow.off('avoidAreaChange', callback) | Window 级 |
保存 mainWindow | 置为 undefined | 防止使用失效窗口 |
保存 avoidAreaCallback | 置为 undefined | 防止残留引用 |
这里的双重 off 有条件判断保护:onWindowStageDestroy() 执行后字段已被清空,后续 onDestroy() 不会再次调用。即使生命周期顺序或异常路径不同,也有更稳的兜底。
onForeground() 与 onBackground() 当前只记录日志,没有在前后台切换时重复注册断点或重新装载页面。这避免了每次回前台都叠加监听。若未来确实需要刷新数据,应区分“首次初始化”和“回前台刷新”,不能直接重跑整个 onCreate()。
十一、用故障矩阵验证,而不是只看一次启动成功
启动链路的测试不能止于“模拟器点开一次”。至少应覆盖冷启动、前后台、旋转或窗口缩放、异常数据和销毁重建。
| 场景 | 预期结果 | 重点证据 |
|---|---|---|
| 全新安装后冷启动 | Splash 正常出现,随后进入 Index | onCreate、onWindowStageCreate、load success 日志 |
| 已有本地学习记录启动 | 主页相关状态恢复 | UserDataManager.init 后的业务页面展示 |
| 系统深色模式启动 | 项目仍按源码锁定浅色且文字可读 | 启动窗口、Splash、主页截图 |
| 手机旋转或窗口缩放 | 断点值和避让区重新计算 | currentBreakpoint、顶部/底部间距 |
| 前后台切换 | 不重复装载 Splash,不叠加监听 | onForeground / onBackground 日志 |
| 销毁后重建 | 无重复回调,无失效窗口访问 | destroy 日志与监听数量 |
| Preferences 内容异常 | 使用默认空数据,应用不闪退 | 首屏可用、默认状态正确 |
| 页面路径写错的测试分支 | loadContent 明确报错 | 错误码与失败日志 |
上架前还应在目标设备范围内完成安装、启动、主流程、退出、卸载烟测。module.json5 声明了 phone、tablet、2in1,不能只凭手机模拟器通过就推断全部设备形态稳定。窗口避让区和媒体查询尤其需要在可用的真实窗口尺寸上验证。
性能方面可记录三个时间点:进入 onCreate、进入 onWindowStageCreate、loadContent 成功。若首屏慢,再细分 UserDataManager.init 与断点注册耗时。先测量,再优化;不要通过删除必要初始化换取表面上的启动速度。
十二、常见问题与定位方法
| 现象 | 先检查 | 可能原因 | 修复方向 |
|---|---|---|---|
| 点击图标后白屏 | loadContent 回调 | 页面路径未注册、首屏构建异常 | 对齐 main_pages.json,查看首个错误 |
| Splash 背景先闪色 | module.json5 启动窗口资源 | startWindow 与页面背景不一致 | 统一两处视觉资源 |
| 主页底部被手势区遮挡 | 避让区值与 px/vp 转换 | 未监听、类型读取不全、页面未消费 | 核对事件类型、最大值和 px2vp |
| 旋转后布局不更新 | 断点监听 | 未注册或回调未写入 AppStorage | 检查三档 listener 与初始匹配 |
| 重建后同一事件触发多次 | destroy 日志与 listener | 注册后未 off / unregister | 保留回调引用并对称释放 |
| 本地数据损坏导致启动异常 | Preferences JSON | 解析异常未兜底 | 恢复为空数组和默认设置 |
| 从主页返回又看到 Splash | 路由方法 | 使用了 pushUrl | 启动页转主页使用 replaceUrl |
| 页面消失后仍跳转 | Splash 定时器 | 未在生命周期中清理 | 保存 timerId 并在消失时 clear |
排查启动问题的原则是抓第一处失败。日志最后一行不一定是根因;应从入口声明、onCreate、窗口创建、首屏装载、首屏生命周期依次确认。只要某一阶段没有到达,后面的页面现象就只是结果。
十三、可复用的启动链路检查清单
将句匠的实现迁移到其他 HarmonyOS 5.0+ ArkTS 项目时,可以按以下清单验收:
module.json5的mainElement、Ability 名称和srcEntry一致;- 首屏和后续主页都已写入
main_pages.json; onCreate()只初始化应用级依赖与共享状态;- 首屏必需数据在
loadContent前已有真实值或安全默认值; - 窗口 API 只在
onWindowStageCreate()之后调用; - 获取主窗口和监听避让区有异常降级;
loadContent回调分别处理失败和成功;- px 值在页面布局边界转换为 vp;
- 注册的窗口与媒体查询监听在销毁阶段释放;
- Splash 延迟任务在页面消失时清理;
- Splash 到主页使用符合返回栈预期的替换路由;
- 冷启动、前后台、窗口变化、异常本地数据和重建均有验证记录。
这套方法的关键不是把所有工作塞进 EntryAbility,而是让每种初始化回到正确生命周期:应用状态归 onCreate,窗口能力归 onWindowStageCreate,页面动画归页面组件,监听释放归对应销毁回调。边界明确后,启动失败可以快速定位,窗口变化不会留下旧回调,首屏也不必承担平台级初始化。
句匠当前实现已经形成一条可复核的本地启动闭环:系统从模块声明找到 EntryAbility,入口恢复用户数据和共享状态,注册断点,窗口创建后同步避让区并装载 Splash,Splash 完成短动画后替换到主页,销毁时释放窗口与媒体查询监听。它解决的是稳定启动与基础适配,不应被包装成账号、云端或多设备流转能力。
AI 辅助声明
本文由 AI 辅助生成和整理,内容已根据 com.jiaweikang.one18 项目中的 EntryAbility.ets、module.json5、main_pages.json、UserDataManager.ets、BreakpointSystem.ets 与 SplashPage.ets 逐项复核;能力描述仅覆盖源码中可验证的本地启动链路。
更多推荐



所有评论(0)