1# 应用恢复开发指导 2 3## 场景介绍 4 5应用在运行中不可避免会产生一些非预期的行为,如运行时抛出未处理的异常和错误,违反框架的调用/运行约束等。 6 7系统默认对异常的处理方式为进程退出,如果应用使用过程中产生了用户数据,直接退出可能会导致用户工作中断,数据丢失。 8如果应用使能了应用恢复功能,并对临时数据进行保存,应用非预期退出后的下一次启动会恢复先前的状态和数据,给用户更连贯的使用体验。这里状态包括应用的页面栈以及onSaveState接口中保存的数据。 9 10API 9上的应用恢复接口支持单Ability的Stage模型应用开发。支持JsError故障时的状态保存与自动重启。 11 12API 10在API 9的基础上新增支持多Ability的Stage模型应用开发。支持AppFreeze故障时的状态保存回调。支持应用被管控模式杀死后,下次启动的状态恢复。 13 14## 接口说明 15 16应用故障恢复接口由appRecovery模块提供,开发者可以通过import引入,详见[开发示例](#开发示例)。 17 18### 应用恢复接口功能介绍 19 20| 接口名称 | 说明 | 21| ------------------------------------------------------------ | ---------------------------------------------------- | 22| enableAppRecovery(restart?: RestartFlag, saveOccasion?: SaveOccasionFlag, saveMode?: SaveModeFlag) : void;<sup>9+</sup> | 使能应用恢复功能,参数按顺序填入。该接口调用后,应用从启动器启动时第一个Ability支持恢复。| 23| saveAppState(): boolean;<sup>9+</sup> | 主动保存当前应用中支持恢复的Ability的状态。 | 24| restartApp(): void;<sup>9+</sup> | 重启当前进程,并启动由**setRestartWant**指定的Ability,如果未指定,将重新拉起处于前台且支持恢复的Ability。 | 25| saveAppState(context?: UIAbilityContext): boolean;<sup>10+</sup> | 主动保存由Context指定的Ability状态。 | 26| setRestartWant(want: Want): void;<sup>10+</sup> | 设置主动调用**restartApp**以及**RestartFlag**不为**NO_RESTART**时重启的Ability。该Ability必须在同一个包名下,且必须为**UiAbility**。 | 27 28由于上述接口可能在故障处理时使用,所以不会返回异常,需要开发者熟悉使用的场景。 29 30**enableAppRecovery:** 需要在应用初始化阶段调用,比如AbilityStage的OnCreate调用赋能。具体其各参数定义详见[参数说明](../reference/apis/js-apis-app-ability-appRecovery.md)。 31 32**saveAppState:** 调用后框架会回调**当前进程中所有支持恢复的Ability**的onSaveState方法,如果在onSaveState方法中同意保存数据,则会将相关数据及Ability的页面栈持久化到应用的本地缓存。如果需要保存指定Ability,则需要指定Ability对应的Context。 33 34**setRestartWant:** 指定由appRecovery发起重启的Ability。 35 36**restartApp:** 调用后框架会杀死当前应用进程,并重新拉起由**setRestartWant**指定的Ability,其中启动原因为APP_RECOVERY。API 9以及未使用**setRestartWant**指定Ability的场景,会拉起最后一个支持恢复且在前台的Ability,如果当前前台的Ability不支持恢复,则应用表现闪退。如果重启的Ability存在已经保存的状态,这些状态数据会在Ability的OnCreate生命周期回调的want参数中作为wantParam属性传入。 37 38### 应用恢复状态管理示意 39从API 10起,应用恢复的场景不仅局限于异常时自动重启。所以需要理解应用何时会加载恢复的状态。 40一句话概括就是**如果应用任务的上次退出不是由用户发起的,且应用存在用于恢复的状态,应用下一次由用户拉起时的启动原因会被设为APP_RECOVERY,并清理该任务的恢复状态。** 41应用恢复状态标识会在状态保存接口主动或者被动调用时设置。在该任务正常退出或者消费了该状态时清理。正常退出目前包括用户按**后退键退出**以及用户**清理最近任务**。 42 43 44 45### 应用卡死的状态保存及恢复 46API 10开始支持应用卡死时的状态保存。JsError故障时,onSaveState接口在主线程进行回调。对于AppFreeze故障,主线程可能处于卡死的状态,onSaveState会在非主线程进行回调。其主要流程如下图: 47 48 49由于卡死时的回调不在JS线程上执行,onSaveState回调中的代码建议不要使用import进来的Native动态库,禁止访问主线程创建的thread_local对象。 50 51### 框架故障管理流程示意 52 53故障管理是应用提升用户体验的重要手段。应用程序框架为开发者提供了故障监听、故障恢复、以及故障查询三种方式来管理应用的故障。 54 55- 故障监听指的是通过[errorManager](../reference/apis/js-apis-app-ability-errorManager.md)注册[ErrorObserver](../reference/apis/js-apis-inner-application-errorObserver.md),监听故障的发生,并通知到监听方。 56 57- 故障恢复指的是[appRecovery](../reference/apis/js-apis-app-ability-appRecovery.md),及故障发生后,将应用重启恢复到故障之前的状态。 58 59- 故障查询指的是[faultLogger](../reference/apis/js-apis-faultLogger.md)通过其查询接口获取当前的故障信息。 60 61下图中并没有标记[faultLogger](../reference/apis/js-apis-faultLogger.md)的调用时机,开发者可以根据应用启动时传入的[LastExitReason](../reference/apis/js-apis-app-ability-abilityConstant.md#abilityconstantlastexitreason)来决定是否调用[faultLogger](../reference/apis/js-apis-faultLogger.md)查询上次的故障信息。 62 63这里建议应用开发者使用[errorManager](../reference/apis/js-apis-app-ability-errorManager.md)对应用的异常进行处理,处理完成后开发者可以选择调用状态保存接口并主动重启应用。 64如果开发者没有注册[ErrorObserver](../reference/apis/js-apis-inner-application-errorObserver.md)也没有使能自动恢复,则按照系统的默认逻辑执行进程退出。用户可以选择从启动器再次打开应用。 65如果开发者使能了自动恢复,框架会首先检查当前故障是否支持状态保存以及开发者是否配置了状态保存,如果支持则会回调[Ability](../reference/apis/js-apis-app-ability-uiAbility.md)的[onSaveState](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityonsavestate)的接口。最后重启应用。 66 67### 应用故障管理接口支持场景 68 69通常的故障类型有JS程序Crash、应用程序卡死、C++程序Crash。Crash故障时应用一般都会被关闭。Freeze故障为应用无响应卡屏场景。应用上层无需关注故障类型,底层恢复框架会根据故障类型来实现不同场景的故障管理。 70 71| 故障名称 | 故障监听 | 状态保存 | 自动重启 | 日志查询 | 72| ----------|--------- |--------- |--------- |--------- | 73| [JS_CRASH](../reference/apis/js-apis-faultLogger.md#faulttype) | 支持|支持|支持|支持| 74| [APP_FREEZE](../reference/apis/js-apis-faultLogger.md#faulttype) | 不支持|支持|支持|支持| 75| [CPP_CRASH](../reference/apis/js-apis-faultLogger.md#faulttype) | 不支持|不支持|不支持|支持| 76 77这里状态保存指的是故障时状态保存,对于应用卡死场景,开发者可以采用定时保存状态或者在Ability切入后台后自动保存的方式最大限度的保护用户数据。 78 79 80 81## 开发示例 82 83### 使能开启自恢复特性 84 85 开发者需要在应用模块初始化时使能appRecovery功能。下面为示例的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### 配置支持恢复的Ability 101Ability的配置清单一般的名字为module.json5。 102```json 103{ 104 "abilities": [ 105 { 106 "name": "EntryAbility", 107 "recoverable": true, 108 }] 109} 110 111``` 112 113### 数据保存和恢复 114 115在使能appRecovery功能后,开发者可以在Ability中采用主动保存状态,主动恢复或者选择被动恢复的方式使用appRecovery功能。 116下面为示例的EntryAbility。 117 118#### 导包 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#### 主动触发保存和恢复 127 128- 定义和注册[ErrorObserver](../reference/apis/js-apis-inner-application-errorObserver.md) callback,具体可参考[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- 数据保存 163 164callback触发appRecovery.saveAppState()调用后,会触发EntryAbility的onSaveState(state, wantParams)函数回调。 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- 数据恢复 181 182callback触发后appRecovery.restartApp()调用后,应用会重启,重启后会走到EntryAbility的onCreate(want, launchParam)函数,保存的数据会在want参数的parameters里。 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- 取消注册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#### 被动保存和恢复 230 231被动保存和恢复依赖恢复框架底层触发,无需注册监听ErrorObserver callback,只需实现Ability的onSaveState接口数据保存和onCreate接口数据恢复流程即可。 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#### 故障Ability的重启恢复标记 265 266发生故障的Ability再次重新启动时,在调度onCreate生命周期里,参数want的parameters成员会有[ABILITY_RECOVERY_RESTART](../reference/apis/js-apis-app-ability-wantConstant.md#wantconstantparams)标记数据,并且值为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 287