• 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.
8Application recovery helps to restore the application state and save temporary data upon next startup in the case of an abnormal process exit, thus providing more consistent user experience. The application state includes two parts, namely, the page stack of the and the data saved in **onSaveState**.
9
10In 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.
11
12In 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.
13
14## Available APIs
15
16The application recovery APIs are provided by the **appRecovery** module, which can be imported via **import**. For details, see [Development Example](#development-example).
17
18### Available APIs
19
20| API                                                      | Description                                                |
21| ------------------------------------------------------------ | ---------------------------------------------------- |
22| enableAppRecovery(restart?: RestartFlag, saveOccasion?: SaveOccasionFlag, saveMode?: SaveModeFlag) : void;<sup>9+</sup> | 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.|
23| saveAppState(): boolean;<sup>9+</sup> | Saves the state of the ability that supports recovery in the current application.|
24| restartApp(): void;<sup>9+</sup> | Restarts the current process and starts the ability specified by **setRestartWant**. If no ability is specified, a foreground ability that supports recovery is restarted.|
25| saveAppState(context?: UIAbilityContext): boolean;<sup>10+</sup> | Saves the ability state specified by **Context**.|
26| setRestartWant(want: Want): void;<sup>10+</sup> | 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**.|
27
28No error will be thrown if the preceding APIs are used in the troubleshooting scenario. The following are some notes on API usage:
29
30**enableAppRecovery**: This API should be called during application initialization. For example, you can call this API in **onCreate** of **AbilityStage**. For details, see [Parameter Description](../reference/apis/js-apis-app-ability-appRecovery.md).
31
32**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.
33
34**setRestartWant**: This API specifies the ability to be restarted by **appRecovery**.
35
36**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. In 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. If 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.
37
38### Application State Management
39Since 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.
40If 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.
41The application recovery status flag is set when **saveAppState** is actively or passively called. The flag is cleared when the application exits normally or the saved state is consumed. (A normal exit is usually triggered by pressing the back key or clearing recent tasks.)
42
43![Application recovery status management](./figures/application_recovery_status_management.png)
44
45### Application State Saving and Restore
46API 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.
47
48![Application recovery from the freezing state](./figures/application_recovery_from_freezing.png)
49When 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.
50
51### Framework Fault Management
52
53Fault 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.
54
55- Fault listening refers to the process of registering an [ErrorObserver](../reference/apis/js-apis-inner-application-errorObserver.md) via [errorManager](../reference/apis/js-apis-app-ability-errorManager.md), listening for faults, and notifying the listener of the faults.
56
57- Fault rectification refers to the process of restoring the application state and data through [appRecovery](../reference/apis/js-apis-app-ability-appRecovery.md).
58
59- Fault query is the process of calling APIs of [faultLogger](../reference/apis/js-apis-faultLogger.md) to obtain the fault information.
60
61The figure below does not illustrate the time when [faultLogger](../reference/apis/js-apis-faultLogger.md) is called. You can refer to the [LastExitReason](../reference/apis/js-apis-app-ability-abilityConstant.md#abilityconstantlastexitreason) passed during application initialization to determine whether to call [faultLogger](../reference/apis/js-apis-faultLogger.md) to query information about the previous fault.
62![Fault rectification process](./figures/fault_rectification.png)
63It is recommended that you call [errorManager](../reference/apis/js-apis-app-ability-errorManager.md) to handle the exception. After the processing is complete, you can call the **saveAppState** API and restart the application.
64If you do not register [ErrorObserver](../reference/apis/js-apis-inner-application-errorObserver.md) or enable application recovery, the application process will exit according to the default processing logic of the system. Users can restart the application from the home screen.
65If 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 invokes [onSaveState](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityonsavestate) of the [Ability](../reference/apis/js-apis-app-ability-uiAbility.md). Finally, the application is restarted.
66
67### Supported Application Recovery Scenarios
68
69Common 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.
70
71| Fault  | Fault Listening | State Saving| Automatic Restart| Log Query|
72| ----------|--------- |--------- |--------- |--------- |
73| [JS_CRASH](../reference/apis/js-apis-faultLogger.md#faulttype) | Supported|Supported|Supported|Supported|
74| [APP_FREEZE](../reference/apis/js-apis-faultLogger.md#faulttype) | Not supported|Supported|Supported|Supported|
75| [CPP_CRASH](../reference/apis/js-apis-faultLogger.md#faulttype) | Not supported|Not supported|Not supported|Supported|
76
77**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.
78
79
80
81## Development Example
82
83### Enabling Application Recovery
84
85   Enable **appRecovery** during application initialization. The following is an example of **AbilityStage**:
86
87```ts
88import AbilityStage from '@ohos.app.ability.AbilityStage'
89import appRecovery from '@ohos.app.ability.appRecovery'
90
91export default class MyAbilityStage extends AbilityStage {
92    onCreate() {
93        console.info("[Demo] MyAbilityStage onCreate")
94        appRecovery.enableAppRecovery(appRecovery.RestartFlag.ALWAYS_RESTART,
95            appRecovery.SaveOccasionFlag.SAVE_WHEN_ERROR | appRecovery.SaveOccasionFlag.SAVE_WHEN_BACKGROUND,
96            appRecovery.SaveModeFlag.SAVE_WITH_FILE);
97    }
98}
99```
100### Enabling Application Recovery for the Specified Abilities
101Generally, the ability configuration list is named **module.json5**.
102```json
103{
104    "abilities": [
105      {
106        "name": "EntryAbility",
107        "recoverable": true,
108      }]
109}
110
111```
112
113### Saving and Restoring Data
114
115After enabling **appRecovery**, you can use this function by either actively or passively saving the application state and restoring data in the ability.
116The following is an example of **EntryAbility**:
117
118#### Importing the Service Package
119
120```ts
121import errorManager from '@ohos.app.ability.errorManager';
122import appRecovery from '@ohos.app.ability.appRecovery';
123import AbilityConstant from '@ohos.app.ability.AbilityConstant';
124```
125
126#### Actively Saving the Application State and Restoring Data
127
128- Define and register the [ErrorObserver](../reference/apis/js-apis-inner-application-errorObserver.md) callback. For details about its usage, see [errorManager](../reference/apis/js-apis-app-ability-errorManager.md).
129
130```ts
131  import appRecovery from '@ohos.app.ability.appRecovery';
132  import errorManager from '@ohos.app.ability.errorManager';
133  import UIAbility from '@ohos.app.ability.UIAbility';
134  import window from '@ohos.window';
135
136  let registerId = -1;
137  let callback: errorManager.ErrorObserver = {
138      onUnhandledException(errMsg) {
139          console.log(errMsg);
140          appRecovery.saveAppState();
141          appRecovery.restartApp();
142      }
143  }
144
145  export default class EntryAbility extends UIAbility {
146      onWindowStageCreate(windowStage: window.WindowStage) {
147          // Main window is created, set main page for this ability
148          console.log("[Demo] EntryAbility onWindowStageCreate")
149          registerId = errorManager.on('error', callback);
150
151          windowStage.loadContent("pages/index", (err, data) => {
152              if (err.code) {
153                  console.error('Failed to load the content. Cause:' + JSON.stringify(err));
154                  return;
155              }
156              console.info('Succeeded in loading the content. Data: ' + JSON.stringify(data))
157          })
158      }
159  }
160```
161
162- Save data.
163
164After the callback triggers **appRecovery.saveAppState()**, **onSaveState(state, wantParams)** of **EntryAbility** is triggered.
165
166```ts
167import AbilityConstant from '@ohos.app.ability.AbilityConstant';
168import UIAbility from '@ohos.app.ability.UIAbility';
169
170export default class EntryAbility extends UIAbility {
171    onSaveState(state:AbilityConstant.StateType, wantParams: Record<string, Object>) {
172        // Ability has called to save app data
173        console.log("[Demo] EntryAbility onSaveState")
174        wantParams["myData"] = "my1234567";
175        return AbilityConstant.OnSaveResult.ALL_AGREE;
176    }
177}
178```
179
180- Restore data.
181
182After 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**.
183
184```ts
185import AbilityConstant from '@ohos.app.ability.AbilityConstant';
186import UIAbility from '@ohos.app.ability.UIAbility';
187import Want from '@ohos.app.ability.Want';
188
189let abilityWant: Want;
190
191export default class EntryAbility extends UIAbility {
192    storage: LocalStorage | undefined = undefined;
193
194    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
195        console.log("[Demo] EntryAbility onCreate")
196        abilityWant = want;
197        if (launchParam.launchReason == AbilityConstant.LaunchReason.APP_RECOVERY) {
198            this.storage = new LocalStorage();
199            if (want.parameters) {
200                let recoveryData = want.parameters["myData"];
201                this.storage.setOrCreate("myData", recoveryData);
202                this.context.restoreWindowStage(this.storage);
203            }
204        }
205    }
206}
207```
208
209- Unregister the **ErrorObserver** callback.
210
211```ts
212import errorManager from '@ohos.app.ability.errorManager';
213import UIAbility from '@ohos.app.ability.UIAbility';
214
215let registerId = -1;
216
217export default class EntryAbility extends UIAbility {
218    onWindowStageDestroy() {
219        // Main window is destroyed, release UI related resources
220        console.log("[Demo] EntryAbility onWindowStageDestroy")
221
222        errorManager.off('error', registerId, (err) => {
223            console.error("[Demo] err:", err);
224        });
225    }
226}
227```
228
229#### Passively Saving the Application State and Restoring Data
230
231This 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.
232
233```ts
234import AbilityConstant from '@ohos.app.ability.AbilityConstant';
235import UIAbility from '@ohos.app.ability.UIAbility';
236import Want from '@ohos.app.ability.Want';
237
238let abilityWant: Want;
239
240export default class EntryAbility extends UIAbility {
241    storage: LocalStorage | undefined = undefined
242    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
243    console.log("[Demo] EntryAbility onCreate")
244        abilityWant = want;
245        if (launchParam.launchReason == AbilityConstant.LaunchReason.APP_RECOVERY) {
246            this.storage = new LocalStorage();
247            if (want.parameters) {
248                let recoveryData = want.parameters["myData"];
249                this.storage.setOrCreate("myData", recoveryData);
250                this.context.restoreWindowStage(this.storage);
251            }
252        }
253    }
254
255    onSaveState(state:AbilityConstant.StateType, wantParams: Record<string, Object>) {
256        // Ability has called to save app data
257        console.log("[Demo] EntryAbility onSaveState")
258        wantParams["myData"] = "my1234567";
259        return AbilityConstant.OnSaveResult.ALL_AGREE;
260    }
261}
262```
263
264#### Restart Flag for the Failed Ability
265
266If the failed ability is restarted again, the [ABILITY_RECOVERY_RESTART](../reference/apis/js-apis-app-ability-wantConstant.md#wantconstantparams) flag will be added as a **parameters** member for the **want** parameter in **onCreate** and its value is **true**.
267
268```ts
269import AbilityConstant from '@ohos.app.ability.AbilityConstant';
270import UIAbility from '@ohos.app.ability.UIAbility';
271import Want from '@ohos.app.ability.Want';
272import wantConstant from '@ohos.app.ability.wantConstant';
273
274export default class EntryAbility extends UIAbility {
275    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
276        if (want.parameters === undefined) {
277            return;
278        }
279        if (want.parameters[wantConstant.Params.ABILITY_RECOVERY_RESTART] != undefined &&
280            want.parameters[wantConstant.Params.ABILITY_RECOVERY_RESTART] == true) {
281            console.log("This ability need to recovery");
282        }
283    }
284}
285```
286