1# 错误管理开发指导 2 3<!--Kit: Performance Analysis Kit--> 4<!--Subsystem: HiviewDFX--> 5<!--Owner: @rr_cn--> 6<!--Designer: @peterhuangyu--> 7<!--Tester: @gcw_KuLfPSbe--> 8<!--Adviser: @foryourself--> 9 10## 场景介绍 11 12当应用的代码存在规范问题或错误时,会在运行中产生异常和错误,如应用未捕获异常等。在错误产生后,应用会异常退出。错误日志通常会保存在用户本地存储设备中,不方便开发者定位问题。所以,应用开发者可以使用错误管理的接口,在应用退出前,及时将相关错误及日志上报到开发者的服务平台来定位问题。 13 14使用errorManager接口监听异常和错误后,应用不会退出,建议在回调函数执行完后,增加同步退出操作,如果只是为了获取错误日志,建议使用[HiAppEvent订阅事件](hiappevent-intro.md)。 15 16## 接口说明 17 18应用错误管理接口由[@ohos.app.ability.errorManager (错误管理模块)](../reference/apis-ability-kit/js-apis-app-ability-errorManager.md)提供,使用接口能力前需注册错误观测器,开发者可以通过import引入,详见[开发示例](#开发示例)。 19 20**错误管理接口功能介绍**: 21 22| 接口名称 | 说明 | 23| -------- | -------- | 24| on(type: "error", observer: ErrorObserver): number | 注册错误监听接口,当系统监测到应用异常时会回调该监听。该接口为同步接口,返回值为注册的监听对象对应的序号。 | 25| off(type: "error", observerId: number, callback: AsyncCallback<void>): void | 以callback的形式解除注册监听,传入的number为之前注册监听时返回的序号。 | 26| off(type: "error", observerId: number): Promise<void> | 以Promise的形式解除注册监听,传入的number为之前注册监听时返回的序号。 | 27| on(type: 'globalErrorOccurred', observer: GlobalObserver): void | 注册进程错误监听接口,当系统监测到应用异常时会回调该监听,该接口为同步接口,即一次注册,全局监听。(**推荐使用**)<br/>说明:从API version 18开始,支持该接口。 | 28| off(type: 'globalErrorOccurred', observer?: GlobalObserver): void | 以callback的形式解除注册监听。(**推荐使用**)<br/>说明:从API version 18开始,支持该接口。 | 29| on(type: 'globalUnhandledRejectionDetected', observer: GlobalObserver): void | 注册进程错误监听接口,当系统监测到应用promise异常时会回调该监听,该接口为同步接口,即一次注册,全局监听。(**推荐使用**)<br/>说明:从API version 18开始,支持该接口。 | 30| off(type: 'globalUnhandledRejectionDetected', observer?: GlobalObserver): void | 以callback的形式解除注册监听。(**推荐使用**)<br/>说明:从API version 18开始,支持该接口。 | 31| on(type: 'loopObserver', timeout: number, observer: LoopObserver): void | 注册主线程消息处理耗时监听器,当系统监测到应用主线程事件处理超时时会回调该监听。<br/>只能在主线程调用,多次注册后,后一次的注册会覆盖前一次的。 | 32| off(type: 'loopObserver', observer?: LoopObserver): void | 以LoopObserver的形式解除应用主线程消息处理耗时监听。 | 33| on(type: 'freeze', observer: FreezeObserver): void | 注册应用主线程freeze监听。只能在主线程调用,重复注册后,后一次的注册会覆盖前一次的。 | 34| off(type: 'freeze', observer?: FreezeObserver): void | 以FreezeObserver的形式解除应用主线程消息处理耗时监听。<br/>说明:从API version 18开始,支持该接口。 | 35 36当采用callback作为异步回调时,可以在callback中进行下一步处理。 37当采用Promise对象返回时,可以在Promise对象中类似地处理接口返回值,具体结果码说明见[解除注册结果码](#解除注册结果码)。 38 39**错误监听(ErrorObserver)接口功能介绍**: 40 41| 接口名称 | 说明 | 42| -------- | -------- | 43| onUnhandledException(errMsg: string): void | 系统回调接口,应用注册后,当应用产生未捕获的异常时的回调。 | 44| onException?(errObject: Error): void | 系统回调接口,应用注册后,当应用产生异常上报js层时的回调。 | 45 46**应用主线程监听(LoopObserver)接口功能介绍**: 47 48| 接口名称 | 说明 | 49| -------- | -------- | 50| onLoopTimeOut?(timeout: number): void | 系统回调接口,应用注册后,当应用主线程处理事件超时的回调。 | 51 52### 解除注册结果码 53 54| 结果码 | 原因 | 55| -------- | -------- | 56| 0 | 正常返回 | 57| -1 | 传入的number参数不存在 | 58| -2 | 参数错误 | 59 60## 开发示例 61 62> **注意:** 63> 64> 建议在异常回调函数处理的最后,增加同步退出操作,以避免多次异常回调。 65 66### 单线程监听场景 67 68```ts 69import { AbilityConstant, errorManager, UIAbility, Want } from '@kit.AbilityKit'; 70import { window } from '@kit.ArkUI'; 71import { process } from '@kit.ArkTS'; 72 73let registerId = -1; 74let callback: errorManager.ErrorObserver = { 75 onUnhandledException: (errMsg) => { 76 console.info(errMsg); 77 }, 78 onException: (errorObj) => { 79 console.info('onException, name: ', errorObj.name); 80 console.info('onException, message: ', errorObj.message); 81 if (typeof(errorObj.stack) == 'string') { 82 console.info('onException, stack: ', errorObj.stack); 83 } 84 //回调函数执行完,采用同步退出方式,避免多次触发异常 85 let pro = new process.ProcessManager(); 86 pro.exit(0); 87 } 88} 89 90let abilityWant: Want; 91 92export default class EntryAbility extends UIAbility { 93 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 94 console.info("[Demo] EntryAbility onCreate"); 95 registerId = errorManager.on("error", callback); 96 abilityWant = want; 97 } 98 99 onDestroy() { 100 console.info("[Demo] EntryAbility onDestroy"); 101 errorManager.off("error", registerId, (result) => { 102 console.info("[Demo] result " + result.code + ";" + result.message); 103 }); 104 } 105 106 onWindowStageCreate(windowStage: window.WindowStage) { 107 // 为已创建的主窗口设置主页面 108 console.info("[Demo] EntryAbility onWindowStageCreate"); 109 110 windowStage.loadContent("pages/index", (err) => { 111 if (err.code) { 112 console.error('Failed to load the content. Cause:' + JSON.stringify(err)); 113 return; 114 } 115 }); 116 } 117 118 onWindowStageDestroy() { 119 // 销毁主窗口,释放相关UI资源 120 console.info("[Demo] EntryAbility onWindowStageDestroy"); 121 } 122 123 onForeground() { 124 // 切换前台 125 console.info("[Demo] EntryAbility onForeground"); 126 } 127 128 onBackground() { 129 // 切换后台 130 console.info("[Demo] EntryAbility onBackground"); 131 } 132}; 133``` 134 135### 进程监听异常场景 136 137```ts 138import { AbilityConstant, errorManager, UIAbility, Want } from '@kit.AbilityKit'; 139import { window } from '@kit.ArkUI'; 140import { process } from '@kit.ArkTS'; 141 142function errorFunc(observer: errorManager.GlobalError) { 143 console.info("[Demo] result name :" + observer.name); 144 console.info("[Demo] result message :" + observer.message); 145 console.info("[Demo] result stack :" + observer.stack); 146 console.info("[Demo] result instanceName :" + observer.instanceName); 147 console.info("[Demo] result instanceType :" + observer.instanceType); 148 //回调函数执行完,采用同步退出方式,避免多次触发异常 149 let pro = new process.ProcessManager(); 150 pro.exit(0); 151} 152 153let abilityWant: Want; 154 155export default class EntryAbility extends UIAbility { 156 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 157 console.info("[Demo] EntryAbility onCreate"); 158 errorManager.on("globalErrorOccurred", errorFunc); 159 abilityWant = want; 160 } 161 162 onDestroy() { 163 console.info("[Demo] EntryAbility onDestroy"); 164 errorManager.off("globalErrorOccurred", errorFunc); 165 } 166 167 onWindowStageCreate(windowStage: window.WindowStage) { 168 // 为已创建的主窗口设置主页面 169 console.info("[Demo] EntryAbility onWindowStageCreate"); 170 171 windowStage.loadContent("pages/index", (err) => { 172 if (err.code) { 173 console.error('Failed to load the content. Cause:' + JSON.stringify(err)); 174 return; 175 } 176 }); 177 } 178 179 onWindowStageDestroy() { 180 // 销毁主窗口,释放相关UI资源 181 console.info("[Demo] EntryAbility onWindowStageDestroy"); 182 } 183 184 onForeground() { 185 // 切换前台 186 console.info("[Demo] EntryAbility onForeground"); 187 } 188 189 onBackground() { 190 // 切换后台 191 console.info("[Demo] EntryAbility onBackground"); 192 } 193}; 194``` 195 196### 进程监听promise异常场景 197 198```ts 199import { AbilityConstant, errorManager, UIAbility, Want } from '@kit.AbilityKit'; 200import { window } from '@kit.ArkUI'; 201import { process } from '@kit.ArkTS'; 202 203function promiseFunc(observer: errorManager.GlobalError) { 204 console.info("[Demo] result name :" + observer.name); 205 console.info("[Demo] result message :" + observer.message); 206 console.info("[Demo] result stack :" + observer.stack); 207 console.info("[Demo] result instanceName :" + observer.instanceName); 208 console.info("[Demo] result instanceType :" + observer.instanceType); 209 //回调函数执行完,采用同步退出方式,避免多次触发异常 210 let pro = new process.ProcessManager(); 211 pro.exit(0); 212} 213 214 215let abilityWant: Want; 216 217export default class EntryAbility extends UIAbility { 218 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 219 console.info("[Demo] EntryAbility onCreate"); 220 errorManager.on("globalUnhandledRejectionDetected", promiseFunc); 221 abilityWant = want; 222 } 223 224 onDestroy() { 225 console.info("[Demo] EntryAbility onDestroy"); 226 errorManager.off("globalUnhandledRejectionDetected", promiseFunc); 227 } 228 229 onWindowStageCreate(windowStage: window.WindowStage) { 230 // 为已创建的主窗口设置主页面 231 console.info("[Demo] EntryAbility onWindowStageCreate"); 232 233 windowStage.loadContent("pages/index", (err) => { 234 if (err.code) { 235 console.error('Failed to load the content. Cause:' + JSON.stringify(err)); 236 return; 237 } 238 }); 239 } 240 241 onWindowStageDestroy() { 242 // 销毁主窗口,释放相关UI资源 243 console.info("[Demo] EntryAbility onWindowStageDestroy"); 244 } 245 246 onForeground() { 247 // 切换前台 248 console.info("[Demo] EntryAbility onForeground"); 249 } 250 251 onBackground() { 252 // 切换后台 253 console.info("[Demo] EntryAbility onBackground"); 254 } 255}; 256``` 257 258### 主线程监听freeze 259 260```ts 261import { AbilityConstant, errorManager, UIAbility, Want } from '@kit.AbilityKit'; 262import { window } from '@kit.ArkUI'; 263import { process } from '@kit.ArkTS'; 264 265// Define freezeCallback 266function freezeCallback() { 267 console.info("freezecallback"); 268} 269 270 271let abilityWant: Want; 272 273export default class EntryAbility extends UIAbility { 274 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 275 console.info("[Demo] EntryAbility onCreate"); 276 errorManager.on("freeze", freezeCallback); 277 abilityWant = want; 278 } 279 280 onDestroy() { 281 console.info("[Demo] EntryAbility onDestroy"); 282 errorManager.off("freeze", freezeCallback); 283 } 284 285 onWindowStageCreate(windowStage: window.WindowStage) { 286 // 为已创建的主窗口设置主页面 287 console.info("[Demo] EntryAbility onWindowStageCreate"); 288 289 windowStage.loadContent("pages/index", (err) => { 290 if (err.code) { 291 console.error('Failed to load the content. Cause:' + JSON.stringify(err)); 292 return; 293 } 294 }); 295 } 296 297 onWindowStageDestroy() { 298 // 销毁主窗口,释放相关UI资源 299 console.info("[Demo] EntryAbility onWindowStageDestroy"); 300 } 301 302 onForeground() { 303 // 切换前台 304 console.info("[Demo] EntryAbility onForeground"); 305 } 306 307 onBackground() { 308 // 切换后台 309 console.info("[Demo] EntryAbility onBackground"); 310 } 311}; 312``` 313