• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 应用恢复开发指导
2
3## 场景介绍
4
5应用在运行中不可避免会产生一些非预期的行为,如运行时抛出未处理的异常和错误,违反框架的调用/运行约束等。
6
7系统默认对异常的处理方式为进程退出,如果应用使用过程中产生了用户数据,直接退出可能会导致用户工作中断,数据丢失。
8
9如果应用在[AbilityStage](../reference/apis-ability-kit/js-apis-app-ability-abilityStage.md#abilitystage)中使能[应用恢复功能](#应用恢复接口功能介绍),并对临时数据进行保存,应用非预期退出后的下一次启动会恢复先前的状态和数据,给用户更连贯的使用体验。这里状态包括应用的页面栈以及onSaveState接口中保存的数据。
10
11API 9上的应用恢复接口支持单UIAbility的Stage模型应用开发。支持JsError故障时的状态保存与自动重启。
12
13API 10在API 9的基础上新增支持多UIAbility的Stage模型应用开发。支持AppFreeze故障时的状态保存回调。支持应用被管控模式杀死后,下次启动的状态恢复。
14
15## 接口说明
16
17应用故障恢复接口由appRecovery模块提供,开发者可以通过import引入,详见[开发步骤](#开发步骤)。
18
19### 应用恢复接口功能介绍
20
21| 接口名称 | 说明 |
22| -------- | -------- |
23| enableAppRecovery(restart?: RestartFlag, saveOccasion?: SaveOccasionFlag, saveMode?: SaveModeFlag) : void | 使能应用恢复功能。 |
24| saveAppState(): boolean | 主动保存当前应用中支持恢复的UIAbility的状态。 |
25| restartApp(): void | 重启当前进程,并启动由**setRestartWant**指定的UIAbility,如果未指定,将重新拉起处于前台且支持恢复的UIAbility。 |
26| saveAppState(context?: UIAbilityContext): boolean | 主动保存由Context指定的UIAbility状态。 |
27| setRestartWant(want: Want): void | 设置主动调用**restartApp**以及**RestartFlag**不为**NO_RESTART**时重启的UIAbility(want的abilityName属性可设置为UIAbility的名称)。该UIAbility必须在同一个包名下。 |
28
29由于上述接口可能在故障处理时使用,所以不会返回异常,需要开发者熟悉使用的场景。具体其各参数定义详见[参数说明](https://gitcode.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-ability-kit/js-apis-app-ability-appRecovery.md)30
31**enableAppRecovery**:需要在应用初始化阶段调用,比如AbilityStage的OnCreate调用。调用该接口后,应用恢复时将按首个支持恢复的UIAbility进行恢复。
32
33**saveAppState**:调用后框架会回调当前进程中所有支持恢复的UIAbility的onSaveState方法。如果在onSaveState方法中同意保存数据,则会将相关数据及UIAbility的页面栈持久化到应用的本地缓存。如果需要保存指定UIAbility,则需要指定UIAbility对应的Context。
34
35**setRestartWant**:指定由appRecovery发起重启的UIAbility。
36
37**restartApp**:调用后框架会杀死当前应用进程,并重新拉起由**setRestartWant**指定的UIAbility,其中启动原因为APP_RECOVERY。
38
39API 9以及未使用**setRestartWant**指定UIAbility的场景,会拉起最后一个支持恢复且在前台的UIAbility,如果当前前台的UIAbility不支持恢复,则应用表现闪退。
40
41如果重启的UIAbility存在已经保存的状态,这些状态数据会在UIAbility的OnCreate生命周期回调的want参数中作为wantParam属性传入。两次重启的间隔应大于一分钟,一分钟之内重复调用此接口只会退出应用不会重启应用。自动重启的行为与主动重启一致。
42
43### 应用恢复状态管理示意
44
45从API 10起,应用恢复的场景不仅局限于异常时自动重启。所以需要理解应用何时会加载恢复的状态。
46
47简而言之,如果应用任务的上次退出不是由用户发起的,且应用存在用于恢复的状态,应用下一次由用户拉起时的启动原因会被设为APP_RECOVERY,并清理该任务的恢复状态。
48
49应用恢复状态标识会在状态保存接口主动或者被动调用时设置。在应用正常退出或者应用异常退出重启后,该状态会被清理。正常退出目前包括用户按后退键退出以及用户清理最近任务。
50
51![20230315112155.png](figures/20230315112155.png)
52
53### 应用卡死的状态保存及恢复
54
55API 10开始支持应用卡死时的状态保存。JsError故障时,onSaveState接口在主线程进行回调。对于AppFreeze故障,主线程可能处于卡死的状态,onSaveState会在非主线程进行回调。其主要流程如下图:
56
57![20230315112235.png](figures/20230315112235.png)
58
59由于卡死时的回调不在JS线程上执行,onSaveState回调中的代码建议不要使用import进来的Native动态库,禁止访问主线程创建的thread_local对象。
60
61### 框架故障管理流程示意
62
63故障管理是应用提升用户体验的重要手段。应用程序框架为开发者提供了故障监听、故障恢复、以及故障查询三种方式来管理应用的故障。
64
65- 故障监听指的是通过[errorManager](../reference/apis-ability-kit/js-apis-app-ability-errorManager.md)注册[ErrorObserver](../reference/apis-ability-kit/js-apis-inner-application-errorObserver.md),监听故障的发生,并通知到监听方。
66
67- 故障恢复指的是[appRecovery](../reference/apis-ability-kit/js-apis-app-ability-appRecovery.md),及故障发生后,将应用重启恢复到故障之前的状态。
68
69- 故障查询指的是[faultLogger](../reference/apis-performance-analysis-kit/js-apis-faultLogger.md)通过其查询接口获取当前的故障信息。
70
71下图中并没有标记faultLogger的调用时机,开发者可以根据应用启动时传入的[LastExitReason](../reference/apis-ability-kit/js-apis-app-ability-abilityConstant.md#lastexitreason)来决定是否调用faultLogger查询上次的故障信息。
72
73![20221106203527](figures/20221106203527.png)
74
75这里建议应用开发者使用errorManager对应用的异常进行处理,处理完成后开发者可以选择调用状态保存接口并主动重启应用。
76
77如果开发者没有注册ErrorObserver也没有使能应用恢复,则按照系统的默认逻辑执行进程退出。用户可以选择从启动器再次打开应用。
78
79如果开发者使能应用恢复,框架会首先检查当前故障是否支持状态保存以及开发者是否配置了状态保存,如果支持则会回调UIAbility的[onSaveState](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onsavestate)的接口。最后重启应用。
80
81### 应用故障管理接口支持场景
82
83通常的故障类型有JS程序Crash、应用程序卡死、C++程序Crash。Crash故障时应用一般都会被关闭。Freeze故障为应用无响应卡屏场景。应用上层无需关注故障类型,底层恢复框架会根据故障类型来实现不同场景的故障管理。
84
85| 故障名称 | 故障监听 | 状态保存 | 自动重启 | 日志查询 |
86| -------- | -------- | -------- | -------- | -------- |
87| [JS_CRASH](../reference/apis-performance-analysis-kit/js-apis-faultLogger.md#faulttype) | 支持 | 支持 | 支持 | 支持 |
88| [APP_FREEZE](../reference/apis-performance-analysis-kit/js-apis-faultLogger.md#faulttype) | API18及以上支持 | 支持 | 支持 | 支持 |
89| [CPP_CRASH](../reference/apis-performance-analysis-kit/js-apis-faultLogger.md#faulttype) | 不支持 | 不支持 | 不支持 | 支持 |
90
91这里状态保存指的是故障时状态保存,对于应用卡死场景,开发者可以采用定时保存状态或者在UIAbility切入后台后自动保存的方式最大限度的保护用户数据。
92
93## 开发步骤
94
95### 使能开启自恢复特性
96
97开发者需要在应用模块初始化时使能appRecovery功能。下面为示例的AbilityStage。
98
99```ts
100import { AbilityStage, appRecovery } from '@kit.AbilityKit';
101
102export default class MyAbilityStage extends AbilityStage {
103    onCreate() {
104        console.info("[Demo] MyAbilityStage onCreate");
105        appRecovery.enableAppRecovery(appRecovery.RestartFlag.ALWAYS_RESTART,
106            appRecovery.SaveOccasionFlag.SAVE_WHEN_ERROR | appRecovery.SaveOccasionFlag.SAVE_WHEN_BACKGROUND,
107            appRecovery.SaveModeFlag.SAVE_WITH_FILE);
108    }
109}
110```
111
112### 配置支持恢复的UIAbility
113
114UIAbility的配置清单一般的名字为module.json5115
116```json
117{
118    "abilities": [
119      {
120        "name": "EntryAbility",
121        "recoverable": true,
122      }]
123}
124```
125
126### 数据保存和恢复
127
128在使能appRecovery功能后,开发者可以在UIAbility中采用主动保存状态,主动恢复或者选择被动恢复的方式使用appRecovery功能。
129
130下面为示例的EntryAbility。
131
132**导包**
133
134```ts
135import { AbilityConstant, appRecovery, errorManager } from '@kit.AbilityKit';
136```
137
138**主动触发保存和恢复**
139
140- 定义和注册[ErrorObserver](../reference/apis-ability-kit/js-apis-inner-application-errorObserver.md) callback,具体可参考[errorManager](../reference/apis-ability-kit/js-apis-app-ability-errorManager.md)里的使用方法。
141
142```ts
143import { appRecovery, errorManager, UIAbility } from '@kit.AbilityKit';
144import { window } from '@kit.ArkUI';
145
146let registerId = -1;
147let callback: errorManager.ErrorObserver = {
148    onUnhandledException(errMsg) {
149    console.log(errMsg);
150    appRecovery.saveAppState();
151    appRecovery.restartApp();
152    }
153}
154
155export default class EntryAbility extends UIAbility {
156    onWindowStageCreate(windowStage: window.WindowStage) {
157    // 为已创建的主窗口设置主页面
158    console.log("[Demo] EntryAbility onWindowStageCreate");
159    registerId = errorManager.on('error', callback);
160
161    windowStage.loadContent("pages/index", (err, data) => {
162        if (err.code) {
163        console.error('Failed to load the content. Cause:' + JSON.stringify(err));
164        return;
165        }
166        console.info('Succeeded in loading the content. Data: ' + JSON.stringify(data));
167    })
168    }
169}
170```
171
172- 数据保存
173
174callback触发appRecovery.saveAppState()调用后,会触发EntryAbility的onSaveState(state, wantParams)函数回调。
175
176```ts
177import { AbilityConstant, UIAbility } from '@kit.AbilityKit';
178
179export default class EntryAbility extends UIAbility {
180    onSaveState(state:AbilityConstant.StateType, wantParams: Record<string, Object>) {
181        // UIAbility已调用以保存应用程序数据
182        console.log("[Demo] EntryAbility onSaveState");
183        wantParams["myData"] = "my1234567";
184        return AbilityConstant.OnSaveResult.ALL_AGREE;
185    }
186}
187```
188
189- 数据恢复
190
191callback触发后appRecovery.restartApp()调用后,应用会重启,重启后会走到EntryAbility的onCreate(want, launchParam)函数,保存的数据会在want参数的parameters里。
192
193```ts
194import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
195
196let abilityWant: Want;
197
198export default class EntryAbility extends UIAbility {
199    storage: LocalStorage | undefined = undefined;
200
201    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
202        console.log("[Demo] EntryAbility onCreate");
203        abilityWant = want;
204        if (launchParam.launchReason == AbilityConstant.LaunchReason.APP_RECOVERY) {
205            this.storage = new LocalStorage();
206            if (want.parameters) {
207                let recoveryData = want.parameters["myData"];
208                this.storage.setOrCreate("myData", recoveryData);
209                this.context.restoreWindowStage(this.storage);
210            }
211        }
212    }
213}
214```
215
216- 取消注册ErrorObserver callback
217
218```ts
219import { errorManager, UIAbility } from '@kit.AbilityKit';
220
221let registerId = -1;
222
223export default class EntryAbility extends UIAbility {
224    onWindowStageDestroy() {
225        // 销毁主窗口,释放相关UI资源
226        console.log("[Demo] EntryAbility onWindowStageDestroy");
227
228        errorManager.off('error', registerId, (err) => {
229            console.error("[Demo] err:", err);
230        });
231    }
232}
233```
234
235**被动保存和恢复**
236
237被动保存和恢复依赖恢复框架底层触发,无需注册监听ErrorObserver callback,只需实现UIAbility的onSaveState接口数据保存和onCreate接口数据恢复流程即可。
238
239```ts
240import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
241
242let abilityWant: Want;
243
244export default class EntryAbility extends UIAbility {
245    storage: LocalStorage | undefined = undefined
246    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
247    console.log("[Demo] EntryAbility onCreate");
248        abilityWant = want;
249        if (launchParam.launchReason == AbilityConstant.LaunchReason.APP_RECOVERY) {
250            this.storage = new LocalStorage();
251            if (want.parameters) {
252                let recoveryData = want.parameters["myData"];
253                this.storage.setOrCreate("myData", recoveryData);
254                this.context.restoreWindowStage(this.storage);
255            }
256        }
257    }
258
259    onSaveState(state:AbilityConstant.StateType, wantParams: Record<string, Object>) {
260        // UIAbility已调用以保存应用程序数据
261        console.log("[Demo] EntryAbility onSaveState");
262        wantParams["myData"] = "my1234567";
263        return AbilityConstant.OnSaveResult.ALL_AGREE;
264    }
265}
266```
267
268**故障UIAbility的重启恢复标记**
269
270发生故障的UIAbility再次重新启动时,在调度onCreate生命周期里,参数want的parameters成员会有[ABILITY_RECOVERY_RESTART](../reference/apis-ability-kit/js-apis-app-ability-wantConstant.md#params)标记数据,并且值为true。
271
272```ts
273import { AbilityConstant, UIAbility, Want, wantConstant } from '@kit.AbilityKit';
274
275export default class EntryAbility extends UIAbility {
276    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
277        if (want.parameters === undefined) {
278            return;
279        }
280        if (want.parameters[wantConstant.Params.ABILITY_RECOVERY_RESTART] != undefined &&
281            want.parameters[wantConstant.Params.ABILITY_RECOVERY_RESTART] == true) {
282            console.log("This ability need to recovery");
283        }
284    }
285}
286```
287