• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Development of Application Recovery
2
3## Overview
4
5During application running, some unexpected behaviors are inevitable. For example, unprocessed exceptions and errors are thrown, and the call or running constraints of the recovery framework are violated.
6
7Process exit is treated as the default exception handling method. However, if user data is generated during application use, process exit may interrupt user operations and cause data loss.
8
9If the [application recovery](#available-apis) functionality is enabled in [AbilityStage](../reference/apis-ability-kit/js-apis-app-ability-abilityStage.md#abilitystage) and temporary data is saved, the previous application state and data will be restored upon next startup in the case of an abnormal exit, providing more consistent user experience. The application state includes two parts, namely, the page stack and the data saved in **onSaveState**.
10
11In API version 9, application recovery is supported only for a single ability of the application developed using the stage model. Application state saving and automatic restart are performed when a JsError occurs.
12
13In API version 10, application recovery is applicable to multiple abilities of an application developed using the stage model. Application state storage and restore are performed when an AppFreeze occurs. If an application is killed in control mode, the application state will be restored upon next startup.
14
15## Available APIs
16
17The application recovery APIs are provided by the appRecovery module, which can be imported via **import**. For details, see [How to Develop](#how-to-develop).
18
19### Available APIs
20
21| API| Description|
22| -------- | -------- |
23| enableAppRecovery(restart?: RestartFlag, saveOccasion?: SaveOccasionFlag, saveMode?: SaveModeFlag) : void | Enables application recovery. After this API is called, the first ability that is displayed when the application is started from the initiator can be restored.|
24| saveAppState(): boolean | Saves the state of the ability that supports recovery in the current application.|
25| restartApp(): void | Restarts the current process and starts the ability specified by **setRestartWant**. If no ability is specified, a foreground ability that supports recovery is restarted.|
26| saveAppState(context?: UIAbilityContext): boolean | Saves the ability state specified by **Context**.|
27| setRestartWant(want: Want): void | Sets the abilities to restart when **restartApp** is actively called and **RestartFlag** is not **NO_RESTART**. The abilities must be under the same bundle name and must be a **UIAbility**.|
28
29No error will be thrown if the preceding APIs are used in the troubleshooting scenario. The following are some notes on API usage: For details about the parameters, see [@ohos.app.ability.appRecovery (Application Recovery)](https://gitcode.com/openharmony/docs/blob/master/en/application-dev/reference/apis-ability-kit/js-apis-app-ability-appRecovery.md).
30
31**enableAppRecovery**: This API should be called during application initialization. For example, you can call this API in **onCreate** of **AbilityStage**.
32
33**saveAppState**: After this API is called, the recovery framework invokes **onSaveState** for all abilities that support recovery in the current process. If you choose to save data in **onSaveState**, the related data and ability page stack are persistently stored in the local cache of the application. To save data of the specified ability, you need to specify the context corresponding to that ability.
34
35**setRestartWant**: This API specifies the ability to be restarted by **appRecovery**.
36
37**restartApp**: After this API is called, the recovery framework kills the current process and restarts the ability specified by **setRestartWant**, with **APP_RECOVERY** set as the startup cause.
38
39In API version 9 and scenarios where an ability is not specified by **setRestartWant**, the last foreground ability that supports recovery is started. If the no foreground ability supports recovery, the application crashes.
40
41If a saved state is available for the restarted ability, the saved state is passed as the **wantParam** attribute in the **want** parameter of the ability's **onCreate** callback. The interval between two restarts must be greater than 1 minute. If this API is called repeatedly within 1 minute, the application exits but does not restart. The behavior of automatic restart is the same as that of proactive restart.
42
43### Application State Management
44
45Since API version 10, application recovery is not limited to automatic restart in the case of an exception. Therefore, you need to understand when the application will load the saved state.
46
47If the last exit of an application is not initiated by a user and a saved state is available for recovery, the startup reason is set to **APP_RECOVERY** when the application is started by the user next time, and the recovery state of the application is cleared.
48
49The application recovery state flag is set when **saveAppState** is actively or passively called. The flag is cleared when the application exits normally or restarts after an abnormal exit. (A normal exit is usually triggered by pressing the back key or clearing recent tasks.)
50
51![20230315112155.png](figures/20230315112155.png)
52
53### Application State Saving and Restore
54
55API version 10 or later supports saving of the application state when an application is suspended. If a JsError occurs, **onSaveState** is called in the main thread. If an AppFreeze occurs, however, the main thread may be suspended, and therefore **onSaveState** is called in a non-main thread. The following figure shows the main service flow.
56
57![20230315112235.png](figures/20230315112235.png)
58
59When the application is suspended, the callback is not executed in the JS thread. Therefore, you are advised not to use the imported dynamic Native library or access the **thread_local** object created by the main thread in the code of the **onSaveState** callback.
60
61### Framework Fault Management
62
63Fault management is an important way for applications to deliver a better user experience. The application framework offers three methods for application fault management: fault listening, fault rectification, and fault query.
64
65- Fault listening refers to the process of registering an [ErrorObserver](../reference/apis-ability-kit/js-apis-inner-application-errorObserver.md) via [errorManager](../reference/apis-ability-kit/js-apis-app-ability-errorManager.md), listening for faults, and notifying the listener of the faults.
66
67- Fault rectification refers to the process of restoring the application state and data through [appRecovery](../reference/apis-ability-kit/js-apis-app-ability-appRecovery.md).
68
69- Fault query is the process of calling APIs of [faultLogger](../reference/apis-performance-analysis-kit/js-apis-faultLogger.md) to obtain the fault information.
70
71The figure below does not illustrate the time when faultLogger is called. You can refer to the [LastExitReason](../reference/apis-ability-kit/js-apis-app-ability-abilityConstant.md#lastexitreason) passed during application initialization to determine whether to call faultLogger to query information about the previous fault.
72
73![20221106203527](figures/20221106203527.png)
74
75You are advised to handle application exceptions using errorManager. After the exceptions are fixed, you can call **saveAppState** and restart the application.
76
77If you do not register ErrorObserver or enable application recovery, the process exits according to the default system logic. Users can restart the application from the home screen.
78
79If you have enabled application recovery, the recovery framework first checks whether application state saving is supported and whether the application state saving is enabled. If so, the recovery framework calls [onSaveState](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md#onsavestate) of the ability. Finally, the application is restarted.
80
81### Supported Application Recovery Scenarios
82
83Common fault types include JavaScript application crash, application freezing, and C++ application crash. Generally, an application is closed when a crash occurs. Application freezing occurs when the application does not respond. The fault type can be ignored for the upper layer of an application. The recovery framework implements fault management in different scenarios based on the fault type.
84
85| Fault| Fault Listening| State Saving| Automatic Restart| Log Query|
86| -------- | -------- | -------- | -------- | -------- |
87| [JS_CRASH](../reference/apis-performance-analysis-kit/js-apis-faultLogger.md#faulttype) | Supported| Supported| Supported| Supported|
88| [APP_FREEZE](../reference/apis-performance-analysis-kit/js-apis-faultLogger.md#faulttype) | This fault is supported since API version 18.| Supported| Supported| Supported|
89| [CPP_CRASH](../reference/apis-performance-analysis-kit/js-apis-faultLogger.md#faulttype) | Not supported| Not supported| Not supported| Supported|
90
91**State Saving** in the table header means saving of the application state when a fault occurs. To protect user data as much as possible when an AppFreeze occurs, you can adopt either the periodic or automatic way, and the latter will save user data when an ability is switched to the background.
92
93## How to Develop
94
95### Enabling Application Recovery
96
97Enable **appRecovery** during application initialization. The following is an example of **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### Enabling Application Recovery for the Specified Abilities
113
114Generally, the ability configuration list is named **module.json5**.
115
116```json
117{
118    "abilities": [
119      {
120        "name": "EntryAbility",
121        "recoverable": true,
122      }]
123}
124```
125
126### Saving and Restoring Data
127
128After enabling **appRecovery**, you can use this function by either actively or passively saving the application state and restoring data in the ability.
129
130The following is an example of **EntryAbility**:
131
132**Importing modules**
133
134```ts
135import { AbilityConstant, appRecovery, errorManager } from '@kit.AbilityKit';
136```
137
138**Actively saving state and restoring data**
139
140- Define and register the [ErrorObserver](../reference/apis-ability-kit/js-apis-inner-application-errorObserver.md) callback. For details about its usage, see [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    // Set the main page for the created main window.
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- Save data.
173
174After the callback triggers **appRecovery.saveAppState()**, **onSaveState(state, wantParams)** of **EntryAbility** is triggered.
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        // The ability is called to save application data.
182        console.log("[Demo] EntryAbility onSaveState");
183        wantParams["myData"] = "my1234567";
184        return AbilityConstant.OnSaveResult.ALL_AGREE;
185    }
186}
187```
188
189- Restore data.
190
191After the callback triggers **appRecovery.restartApp()**, the application is restarted. After the restart, **onCreate(want, launchParam)** of **EntryAbility** is called, and the saved data is stored in **parameters** of **want**.
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- Unregister the **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        // Destroy the main window and release related UI resources.
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**Passively saving state and restoring data**
236
237This is triggered by the recovery framework. You do not need to register an **ErrorObserver** callback. You only need to implement **onSaveState** for application state saving and **onCreate** for data restore.
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        // The ability is called to save application data.
261        console.log("[Demo] EntryAbility onSaveState");
262        wantParams["myData"] = "my1234567";
263        return AbilityConstant.OnSaveResult.ALL_AGREE;
264    }
265}
266```
267
268**Restart flag for the failed ability**
269
270If the failed ability is restarted again, the [ABILITY_RECOVERY_RESTART](../reference/apis-ability-kit/js-apis-app-ability-wantConstant.md#params) flag will be added as a **parameters** member for the **want** parameter in **onCreate** and its value is **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