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 52 53### 应用卡死的状态保存及恢复 54 55API 10开始支持应用卡死时的状态保存。JsError故障时,onSaveState接口在主线程进行回调。对于AppFreeze故障,主线程可能处于卡死的状态,onSaveState会在非主线程进行回调。其主要流程如下图: 56 57 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 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.json5。 115 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