App 崩了以后能自动恢复吗?enableAppRecovery 最小实验【鸿蒙心迹】

你是不是也在想——“鸿蒙这么火,我能不能学会?”
答案是:当然可以!
这个专栏专为零基础小白设计,不需要编程基础,也不需要懂原理、背术语。我们会用最通俗易懂的语言、最贴近生活的案例,手把手带你从安装开发工具开始,一步步学会开发自己的鸿蒙应用。
不管你是学生、上班族、打算转行,还是单纯对技术感兴趣,只要你愿意花一点时间,就能在这里搞懂鸿蒙开发,并做出属于自己的App!
📌 关注本专栏《零基础学鸿蒙开发》,一起变强!
每一节内容我都会持续更新,配图+代码+解释全都有,欢迎点个关注,不走丢,我是小白酷爱学习,我们一起上路 🚀
全文目录:
前言
App 崩溃是每个开发者都不想碰见却又绕不开的问题。Crash 发生的瞬间,用户正在填写的表单、刚刚切换到的页面状态、未来得及提交的操作——全部丢失。用户重启 App,一切从头。
HarmonyOS 从 API version 9 起,在 Ability Kit 中提供了 appRecovery 模块,允许应用在故障前保存关键状态,并在下次被框架重新拉起时恢复这些状态。这篇文章用一个最小实验把这个流程跑通。
一、这个能力要解决什么问题??
先说清楚它的范围。
appRecovery 不是捕获异常、不是 try-catch 的替代品,也不会阻止 Crash 发生。它做的事情是:
在 Crash 发生的那一刻,框架介入,保存页面栈和开发者指定的自定义数据;然后重新拉起 App;App 启动时,用保存的数据把界面状态还原回去。
对用户来说,体验上的变化是:Crash 之后不再是冷启动落在首页,而是重新回到接近崩溃前的页面状态。
这个能力覆盖两类故障:
- JS_CRASH:ArkTS 运行时抛出的未捕获异常;
- APP_FREEZE:应用无响应(ANR 类问题)。
二、先把官方约束弄清楚
支持的版本和设备
| 条件 | 说明 |
|---|---|
| API 版本 | API version 9 及以上 |
| 应用模型 | Stage 模型,不支持 FA 模型 |
| 支持设备 | phone / tablet / 2in1 |
| Ability 类型 | UIAbility;ServiceExtensionAbility、DataShareExtensionAbility 等非 UI 类 Ability 不支持 |
多进程、多 UIAbility 的限制
官方文档明确说明:
当前只支持单进程单 UIAbility 的应用进行故障恢复。若应用存在多个 UIAbility,只能恢复发生故障的那个 UIAbility。
如果应用存在多个进程,需要在每个进程对应的 AbilityStage 的
onCreate中分别调用enableAppRecovery,才能使该进程的恢复功能生效。
这一点容易被忽略,后面还会单独提。
核心接口
所属模块:appRecovery,来自 @kit.AbilityKit。
| 接口 | 说明 | API 版本 |
|---|---|---|
enableAppRecovery(restart?, saveOccasion?, saveMode?) | 开启故障恢复能力 | 9+ |
restoreAppState() | 恢复上一次保存的状态 | 9+ |
saveAppState(context?) | 主动保存当前状态 | 10+(旧版无参数的接口已废弃) |
setRestartWant(want) | 指定故障重启时使用的 Want | 11+ |
配合使用的生命周期回调:
| 回调 | 位置 | 说明 |
|---|---|---|
onSaveState(reason, wantParam) | UIAbility | 故障或迁移触发时,框架回调此方法让开发者写入自定义数据 |
三、流程图:崩溃到恢复的完整路径
AbilityStage.onCreate()
└─ appRecovery.enableAppRecovery() ← 开启恢复能力
│
│(某一时刻发生 JS_CRASH 或 APP_FREEZE)
▼
UIAbility.onSaveState() ← 框架回调,保存自定义数据
└─ return AbilityConstant.OnSaveResult.APP_RECOVERY
│
│(框架自动重新拉起 App)
▼
UIAbility.onCreate()
└─ launchParam.launchReason === AbilityConstant.LaunchReason.APP_RECOVERY
└─ appRecovery.restoreAppState() ← 恢复状态
整个链路分布在三个地方:AbilityStage、UIAbility 的 onSaveState、UIAbility 的 onCreate。
四、最小实验:三步跑通完整流程
目标是验证下面这件事:开启恢复能力之后,触发一次可控的 Crash,观察 App 被重新拉起并尝试恢复状态。
第一步:在 AbilityStage 中开启恢复
// entry/src/main/ets/myabilitystage/MyAbilityStage.ets
import { AbilityStage, appRecovery } from '@kit.AbilityKit';
export default class MyAbilityStage extends AbilityStage {
onCreate(): void {
// 发生任意故障时重启;故障发生前保存状态;写入本地文件缓存
appRecovery.enableAppRecovery(
appRecovery.RestartFlag.ALWAYS_RESTART,
appRecovery.SaveOccasionFlag.SAVE_WHEN_ERROR,
appRecovery.SaveModeFlag.SAVE_WITH_FILE
);
}
}
这里真正需要关注的是三个枚举参数的含义,下面单独说明。
module.json5 中需要把 srcEntry 指向这个 AbilityStage:
// entry/src/main/module.json5
{
"module": {
"name": "entry",
"type": "entry",
"srcEntry": "./ets/myabilitystage/MyAbilityStage.ets",
...
}
}
如果不配置 srcEntry,AbilityStage 就不会被加载,enableAppRecovery 也就不会执行。
第二步:在 UIAbility 中保存自定义数据并检测恢复启动
// entry/src/main/ets/entryability/EntryAbility.ets
import { AbilityConstant, UIAbility, Want, appRecovery } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 判断是否为故障恢复启动
if (launchParam.launchReason === AbilityConstant.LaunchReason.APP_RECOVERY) {
hilog.info(0x0000, 'AppRecovery', '检测到故障恢复启动,开始恢复状态');
// 恢复上次保存的页面栈和自定义数据
const success = appRecovery.restoreAppState();
hilog.info(0x0000, 'AppRecovery', `restoreAppState 结果: ${success}`);
}
}
// 故障(或跨设备迁移)触发时,框架回调此方法
onSaveState(
reason: AbilityConstant.StateType,
wantParam: Record<string, Object>
): AbilityConstant.OnSaveResult {
// 只处理故障恢复触发的保存
if (reason === AbilityConstant.StateType.APP_RECOVERY) {
// 把需要恢复的关键数据写进 wantParam
wantParam['lastPage'] = 'detail';
wantParam['inputText'] = '用户正在填写的内容';
hilog.info(0x0000, 'AppRecovery', '故障前状态已保存');
}
// 告知框架:同意进行故障恢复保存
return AbilityConstant.OnSaveResult.APP_RECOVERY;
}
}
真正需要关注的几个地方:
launchParam.launchReason的值必须和AbilityConstant.LaunchReason.APP_RECOVERY对比,不要硬编码数字;onSaveState的返回值必须是AbilityConstant.OnSaveResult.APP_RECOVERY,返回其他值框架不会保存故障现场;wantParam里写入的自定义数据,在restoreAppState()之后,框架会通过 Want 传回给 Ability,可以在onCreate的want.parameters中读取。
第三步:触发一次可控崩溃用来验证
这里只是验证流程,在页面上放一个按钮,点击时制造一个未捕获异常:
// 页面 Index.ets(仅用于验证,实际项目请勿这样用)
import { hilog } from '@kit.PerformanceAnalysisKit';
@Entry
@Component
struct Index {
@State private counter: number = 0;
build() {
Column({ space: 20 }) {
Text(`计数器:${this.counter}`)
.fontSize(24)
Button('计数 +1')
.onClick(() => {
this.counter += 1;
})
Button('模拟 Crash(仅调试用)')
.fontColor(Color.Red)
.onClick(() => {
hilog.info(0x0000, 'AppRecovery', `崩溃前计数器值:${this.counter}`);
// 制造未捕获异常
const arr: string[] = [];
console.log(arr[999].length.toString());
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
点击"模拟 Crash"按钮之后,App 会因未捕获异常触发 JS_CRASH,框架回调 onSaveState 保存数据,然后重新拉起 App。重新拉起后,onCreate 中会检测到 launchReason === APP_RECOVERY,并调用 restoreAppState()。
五、三个枚举参数拆开看
enableAppRecovery 的三个可选参数每个都有独立意义,容易一起传进去但不清楚各自控制什么。
RestartFlag:故障时要不要重启
| 枚举值 | 说明 |
|---|---|
ALWAYS_RESTART(默认) | 发生任意故障时都重启 |
NO_RESTART_ON_JS_CRASH | JS_CRASH 时不重启 |
NO_RESTART_ON_APP_FREEZE | APP_FREEZE 时不重启 |
NO_RESTART | 任意故障都不重启 |
如果设置为 NO_RESTART,框架依然会触发 onSaveState(如果配置了 SAVE_WHEN_ERROR),但不会重新拉起 App。这种场景适合只想保存 Crash 现场数据、不希望自动重启的应用。
SaveOccasionFlag:什么时候保存状态
| 枚举值 | 说明 |
|---|---|
SAVE_WHEN_ERROR(默认) | 故障发生时才保存 |
SAVE_WHEN_BACKGROUND | 应用切入后台时也保存 |
两个值可以通过位运算组合:
appRecovery.SaveOccasionFlag.SAVE_WHEN_ERROR | appRecovery.SaveOccasionFlag.SAVE_WHEN_BACKGROUND
这样做的好处是:即使 Crash 发生在后台,也有最近的一次后台保存数据可以恢复。
SaveModeFlag:保存在哪里
| 枚举值 | 说明 |
|---|---|
SAVE_WITH_FILE(默认) | 写入本地文件缓存 |
SAVE_WITH_SHARED_MEMORY | 保存在内存中 |
选 SAVE_WITH_FILE 的数据在 App 进程被杀死后仍然存在;选 SAVE_WITH_SHARED_MEMORY 的数据进程退出后即消失,只适合进程内重启的场景。
六、容易出错的地方
AbilityStage 没有被加载
enableAppRecovery 必须在 AbilityStage 的 onCreate 中调用,这是官方文档明确的调用时机。如果 module.json5 中没有配置 srcEntry,AbilityStage 不会被实例化,这一步就永远不会执行,恢复能力始终不生效。
这个问题不会有任何报错,只是静默失效,比较难发现。
onSaveState 的返回值写错
如果 onSaveState 返回的不是 AbilityConstant.OnSaveResult.APP_RECOVERY,框架不会保存故障现场数据。有的开发者会误写成 ALL_AGREE(值为 0),这个值用于跨设备迁移保存,不是应用故障恢复场景的正确返回值。
自定义数据的读取位置
onSaveState 写入 wantParam 的数据,恢复后并不是通过 appRecovery 模块读取的,而是通过 onCreate 接收到的 want 对象的 parameters 字段读取。restoreAppState() 负责恢复页面栈,自定义数据要从 want.parameters 中拿。
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (launchParam.launchReason === AbilityConstant.LaunchReason.APP_RECOVERY) {
appRecovery.restoreAppState();
// 自定义数据从 want.parameters 中读取
const lastPage = want.parameters?.['lastPage'];
const inputText = want.parameters?.['inputText'];
}
}
多 UIAbility 只能恢复故障的那个
官方文档说明,若应用有多个 UIAbility,恢复的只是发生故障的那一个。其他 UIAbility 的状态不会被一并恢复,业务层需要自行处理剩余的状态初始化逻辑。
七、排查恢复没有生效时的检查顺序
如果 App Crash 后没有被重新拉起,或者拉起后没有执行恢复逻辑,按下面顺序检查:
- API 版本:确认项目
minApiVersion不低于 9,Stage 模型。 - 设备类型:appRecovery 只支持 phone/tablet/2in1,不支持其他设备形态。
- srcEntry 配置:
module.json5中srcEntry是否正确指向自定义的 AbilityStage。 - enableAppRecovery 调用位置:是否在 AbilityStage 的
onCreate中调用,不是在 UIAbility 里。 - RestartFlag:是否设置了
NO_RESTART或不匹配故障类型的标志。 - onSaveState 返回值:是否返回了
AbilityConstant.OnSaveResult.APP_RECOVERY。 - launchReason 判断:
onCreate中是否正确判断了launchParam.launchReason === AbilityConstant.LaunchReason.APP_RECOVERY。 - 日志确认:在
onSaveState和onCreate中打印 hilog,确认两个回调是否都被执行。
开发经验总结
-
enableAppRecovery必须在 AbilityStage 的onCreate中调用,module.json5的srcEntry是前提条件,缺少这个配置会静默失效。 -
onSaveState返回AbilityConstant.OnSaveResult.APP_RECOVERY才会触发故障现场保存,返回其他值无效。 -
页面栈由
restoreAppState()恢复;自定义数据通过want.parameters读取,两者是分开的。 -
SaveModeFlag.SAVE_WITH_SHARED_MEMORY只适用于进程内重启场景,数据在进程退出后消失;如果希望重启后仍能恢复,使用SAVE_WITH_FILE。 -
这个能力目前只支持单进程单 UIAbility,多 UIAbility 应用在设计恢复逻辑时需要考虑未故障的 UIAbility 的状态如何单独处理。
如果你的应用本身有多个 UIAbility,可以留意一下:当某一个 UIAbility 发生 Crash 被恢复重启之后,另外几个 UIAbility 此时的栈状态是否还在你的预期之内——这类情况比单 UIAbility 应用的恢复场景要复杂一些,值得单独梳理一遍。
❤️ 如果本文帮到了你…
- 请点个赞,让我知道你还在坚持阅读技术长文!
- 请收藏本文,因为你以后一定还会用上!
- 如果你在学习过程中遇到bug,请留言,我帮你踩坑!
更多推荐



所有评论(0)