1# 订阅崩溃事件(C/C++) 2<!--Kit: Performance Analysis Kit--> 3<!--Subsystem: HiviewDFX--> 4<!--Owner: @chenshi51--> 5<!--Designer: @Maplestory--> 6<!--Tester: @yufeifei--> 7<!--Adviser: @foryourself--> 8 9## 简介 10 11本文介绍如何使用HiAppEvent提供的C/C++接口订阅应用崩溃事件。详细使用说明请参考[HiAppEvent C API文档](../reference/apis-performance-analysis-kit/capi-hiappevent-h.md)。 12 13> **说明:** 14> 15> 使用C/C++接口订阅JsError和NativeCrash崩溃事件。 16 17## 接口说明 18 19| 接口名 | 描述 | 20| -------- | -------- | 21| int OH_HiAppEvent_AddWatcher(HiAppEvent_Watcher \*watcher) | 添加应用事件观察者,以添加对应用事件的订阅。 | 22| int OH_HiAppEvent_RemoveWatcher(HiAppEvent_Watcher \*watcher) | 移除应用事件观察者,以移除对应用事件的订阅。 | 23 24## 开发步骤 25 26### 添加事件观察者 27 28**在应用启动后,在执行业务逻辑前添加事件观察者,以确保订阅到崩溃事件。否则,应用可能因崩溃而退出,无法订阅崩溃事件。** 29 30以用户点击按钮触发崩溃事件为例,开发步骤如下: 31 321. 获取示例工程的依赖项jsoncpp。 33 34 参考[三方开源库jsoncpp代码仓](https://github.com/open-source-parsers/jsoncpp)README中**Amalgamated source**部分,获取jsoncpp.cpp、json.h和json-forwards.h三个文件。 35 362. 新建Native C++工程,并将上述文件导入到新建工程,目录结构如下。 37 38 ```yml 39 entry: 40 src: 41 main: 42 cpp: 43 - json: 44 - json.h 45 - json-forwards.h 46 - types: 47 libentry: 48 - index.d.ts 49 - CMakeLists.txt 50 - napi_init.cpp 51 - jsoncpp.cpp 52 ets: 53 - entryability: 54 - EntryAbility.ets 55 - pages: 56 - Index.ets 57 ``` 58 593. 在"CMakeLists.txt"文件中,添加源文件和动态库。 60 61 ```cmake 62 # 新增jsoncpp.cpp(解析订阅事件中的json字符串)源文件 63 add_library(entry SHARED napi_init.cpp jsoncpp.cpp) 64 # 新增动态库依赖libhiappevent_ndk.z.so和libhilog_ndk.z.so(日志输出) 65 target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libhiappevent_ndk.z.so) 66 ``` 67 684. 在"napi_init.cpp"文件中,导入依赖文件,并定义LOG_TAG。 69 70 ```c++ 71 #include "napi/native_api.h" 72 #include "json/json.h" 73 #include "hilog/log.h" 74 #include "hiappevent/hiappevent.h" 75 76 #undef LOG_TAG 77 #define LOG_TAG "testTag" 78 ``` 79 805. 订阅系统事件。 81 82 - onReceive类型观察者 83 84 在"napi_init.cpp"文件中,定义onReceive类型观察者的方法: 85 86 ```c++ 87 //定义一个变量,缓存创建的观察者指针。 88 static HiAppEvent_Watcher *systemEventWatcher; 89 90 static void OnReceive(const char *domain, const struct HiAppEvent_AppEventGroup *appEventGroups, uint32_t groupLen) { 91 for (int i = 0; i < groupLen; ++i) { 92 for (int j = 0; j < appEventGroups[i].infoLen; ++j) { 93 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.domain=%{public}s", appEventGroups[i].appEventInfos[j].domain); 94 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.name=%{public}s", appEventGroups[i].appEventInfos[j].name); 95 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.eventType=%{public}d", appEventGroups[i].appEventInfos[j].type); 96 if (strcmp(appEventGroups[i].appEventInfos[j].domain, DOMAIN_OS) == 0 && 97 strcmp(appEventGroups[i].appEventInfos[j].name, EVENT_APP_CRASH) == 0) { 98 Json::Value params; 99 Json::Reader reader(Json::Features::strictMode()); 100 Json::FastWriter writer; 101 if (reader.parse(appEventGroups[i].appEventInfos[j].params, params)) { 102 auto time = params["time"].asInt64(); 103 auto crashType = params["crash_type"].asString(); 104 auto foreground = params["foreground"].asBool(); 105 auto bundleVersion = params["bundle_version"].asString(); 106 auto bundleName = params["bundle_name"].asString(); 107 auto pid = params["pid"].asInt(); 108 auto uid = params["uid"].asInt(); 109 auto uuid = params["uuid"].asString(); 110 auto exception = writer.write(params["exception"]); 111 auto hilogSize = params["hilog"].size(); 112 auto externalLog = writer.write(params["external_log"]); 113 auto logOverLimit = params["log_over_limit"].asBool(); 114 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.time=%{public}lld", time); 115 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.crash_type=%{public}s", crashType.c_str()); 116 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.foreground=%{public}d", foreground); 117 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_version=%{public}s", bundleVersion.c_str()); 118 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_name=%{public}s", bundleName.c_str()); 119 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.pid=%{public}d", pid); 120 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uid=%{public}d", uid); 121 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uuid=%{public}s", uuid.c_str()); 122 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.exception=%{public}s", exception.c_str()); 123 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.hilog.size=%{public}d", hilogSize); 124 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.external_log=%{public}s", externalLog.c_str()); 125 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.log_over_limit=%{public}d", logOverLimit); 126 } 127 } 128 } 129 } 130 } 131 132 static napi_value RegisterWatcher(napi_env env, napi_callback_info info) { 133 // 开发者自定义观察者名称,系统根据名称识别不同的观察者。 134 systemEventWatcher = OH_HiAppEvent_CreateWatcher("onReceiverWatcher"); 135 // 订阅的事件为EVENT_APP_CRASH。 136 const char *names[] = {EVENT_APP_CRASH}; 137 // 此处开发者订阅了系统事件EVENT_APP_CRASH。 138 OH_HiAppEvent_SetAppEventFilter(systemEventWatcher, DOMAIN_OS, 0, names, 1); 139 // 开发者通过调用OH_HiAppEvent_SetWatcherOnReceive函数设置已实现的回调函数,观察者接收到事件后会触发OnReceive回调。 140 OH_HiAppEvent_SetWatcherOnReceive(systemEventWatcher, OnReceive); 141 // 启动观察者以监听事件。 142 OH_HiAppEvent_AddWatcher(systemEventWatcher); 143 return {}; 144 } 145 ``` 146 147 - onTrigger类型观察者 148 149 在"napi_init.cpp"文件中,定义OnTrigger类型观察者: 150 151 ```c++ 152 //定义一个变量,用来缓存创建的观察者指针。 153 static HiAppEvent_Watcher *systemEventWatcher; 154 155 // 开发者需要实现一个回调函数`OnTake`来获取已监听事件,其中`events`指针仅在该函数内有效。 156 static void OnTake(const char *const *events, uint32_t eventLen) { 157 Json::Reader reader(Json::Features::strictMode()); 158 Json::FastWriter writer; 159 for (int i = 0; i < eventLen; ++i) { 160 Json::Value eventInfo; 161 if (reader.parse(events[i], eventInfo)) { 162 auto domain = eventInfo["domain_"].asString(); 163 auto name = eventInfo["name_"].asString(); 164 auto type = eventInfo["type_"].asInt(); 165 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.domain=%{public}s", domain.c_str()); 166 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.name=%{public}s", name.c_str()); 167 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.eventType=%{public}d", type); 168 if (domain == DOMAIN_OS && name == EVENT_APP_CRASH) { 169 auto time = eventInfo["time"].asInt64(); 170 auto crashType = eventInfo["crash_type"].asString(); 171 auto foreground = eventInfo["foreground"].asBool(); 172 auto bundleVersion = eventInfo["bundle_version"].asString(); 173 auto bundleName = eventInfo["bundle_name"].asString(); 174 auto pid = eventInfo["pid"].asInt(); 175 auto uid = eventInfo["uid"].asInt(); 176 auto uuid = eventInfo["uuid"].asString(); 177 auto exception = writer.write(eventInfo["exception"]); 178 auto hilogSize = eventInfo["hilog"].size(); 179 auto externalLog = writer.write(eventInfo["external_log"]); 180 auto logOverLimit = eventInfo["log_over_limit"].asBool(); 181 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.time=%{public}lld", time); 182 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.crash_type=%{public}s", crashType.c_str()); 183 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.foreground=%{public}d", foreground); 184 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_version=%{public}s", bundleVersion.c_str()); 185 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.bundle_name=%{public}s", bundleName.c_str()); 186 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.pid=%{public}d", pid); 187 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uid=%{public}d", uid); 188 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.uuid=%{public}s", uuid.c_str()); 189 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.exception=%{public}s", exception.c_str()); 190 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.hilog.size=%{public}d", hilogSize); 191 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.external_log=%{public}s", externalLog.c_str()); 192 OH_LOG_INFO(LogType::LOG_APP, "HiAppEvent eventInfo.params.log_over_limit=%{public}d", logOverLimit); 193 } 194 } 195 } 196 } 197 198 // 开发者应实现订阅回调函数,用以对获取的事件打点数据进行自定义处理。 199 static void OnTrigger(int row, int size) { 200 // 接收回调后,获取指定数量的事件。 201 OH_HiAppEvent_TakeWatcherData(systemEventWatcher, row, OnTake); 202 } 203 204 static napi_value RegisterWatcher(napi_env env, napi_callback_info info) { 205 // 开发者自定义观察者名称,系统根据不同的名称来识别不同的观察者。 206 systemEventWatcher = OH_HiAppEvent_CreateWatcher("onTriggerWatcher"); 207 // 设置订阅的事件为EVENT_APP_CRASH。 208 const char *names[] = {EVENT_APP_CRASH}; 209 // 开发者订阅感兴趣的事件,此处订阅了系统事件。 210 OH_HiAppEvent_SetAppEventFilter(systemEventWatcher, DOMAIN_OS, 0, names, 1); 211 // 开发者设置已实现的回调函数,需OH_HiAppEvent_SetTriggerCondition设置的条件满足方可触发。 212 OH_HiAppEvent_SetWatcherOnTrigger(systemEventWatcher, OnTrigger); 213 // 设置订阅触发回调的条件:当设置新增事件打点数量为1时,触发onTrigger回调。 214 OH_HiAppEvent_SetTriggerCondition(systemEventWatcher, 1, 0, 0); 215 // 启动观察者以监听订阅的事件。 216 OH_HiAppEvent_AddWatcher(systemEventWatcher); 217 return {}; 218 } 219 ``` 220 2216. 将RegisterWatcher注册为ArkTS接口。 222 223 在"napi_init.cpp"文件中,将RegisterWatcher注册为ArkTS接口: 224 225 ```c++ 226 static napi_value Init(napi_env env, napi_value exports) 227 { 228 napi_property_descriptor desc[] = { 229 { "registerWatcher", nullptr, RegisterWatcher, nullptr, nullptr, nullptr, napi_default, nullptr } 230 }; 231 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 232 return exports; 233 } 234 ``` 235 236 在"index.d.ts"文件中,定义ArkTS接口: 237 238 ```typescript 239 export const registerWatcher: () => void; 240 ``` 241 2427. 在"EntryAbility.ets"文件的onCreate()函数中添加接口调用。 243 244 ```typescript 245 // 导入依赖模块 246 import testNapi from 'libentry.so' 247 248 // 在onCreate()函数中新增接口调用 249 // 启动时,注册系统事件观察者 250 testNapi.registerWatcher(); 251 ``` 252 2538. 在"Index.ets"文件中,新增按钮触发崩溃事件。 254 255 ```typescript 256 Button("appCrash").onClick(() => { 257 JSON.parse(""); 258 }) 259 ``` 260 2619. 点击运行按钮,启动应用工程。在应用界面中点击“appCrash”按钮,触发崩溃事件。系统生成相应的崩溃日志并进行回调。 262 263> **说明:** 264> 265> JsError通过进程内采集故障信息触发回调,速度快。NativeCrash采取进程外采集故障信息,平均耗时约2秒,具体受业务线程数量和进程间通信影响。订阅崩溃事件后,故障信息采集完成会异步上报,不阻塞当前业务。 266 267### 验证观察者是否订阅到崩溃事件 268 269在应用未主动捕获崩溃异常和主动捕获崩溃异常的两种场景中,崩溃事件的回调时机不同。开发者需要在每种情况下验证是否订阅到崩溃事件。 270 271**应用未主动捕获崩溃异常场景** 272 273若应用未主动捕获崩溃异常,则系统处理崩溃后应用将退出。**应用下次启动时**,HiAppEvent将崩溃事件上报给已注册的监听,完成回调。 274 275**应用主动捕获崩溃异常场景** 276 277若应用主动捕获崩溃异常,HiAppEvent事件将在**应用退出前**触发回调,例如: 278 2791. 异常处理中未主动退出的应用崩溃后不会退出。 280 281 采用[errorManger.on](../reference/apis-ability-kit/js-apis-app-ability-errorManager.md#errormanageronerror)方法捕获异常会导致JsError类型的崩溃事件在应用退出前触发回调。若应用注册[崩溃信号](cppcrash-guidelines.md#系统处理的崩溃信号)处理函数但未主动退出,会导致NativeCrash类型的崩溃事件在应用退出前触发回调。 282 2832. 异常处理耗时过长,会导致应用退出延迟。 284 285在开发调试阶段,HiAppEvent上报事件完成回调后,可在DevEco Studio的HiLog窗口查看订阅的崩溃事件内容。 286 287```text 288HiAppEvent eventInfo.domain=OS 289HiAppEvent eventInfo.name=APP_CRASH 290HiAppEvent eventInfo.eventType=1 291HiAppEvent eventInfo.params.time=1502032265088 292HiAppEvent eventInfo.params.crash_type=JsError 293HiAppEvent eventInfo.params.foreground=1 294HiAppEvent eventInfo.params.bundle_version=1.0.0 295HiAppEvent eventInfo.params.bundle_name=com.example.myapplication 296HiAppEvent eventInfo.params.pid=19237 297HiAppEvent eventInfo.params.uid=20010043 298HiAppEvent eventInfo.params.uuid=cc0f062e1b28c1fd2c817fafab5e8ca3207925b4bdd87c43ed23c60029659e01 299HiAppEvent eventInfo.params.exception={"message":"Unexpected Text in JSON","name":"SyntaxError","stack":"at anonymous (entry/src/main/ets/pages/Index.ets:16:11)"} 300HiAppEvent eventInfo.params.hilog.size=110 301HiAppEvent eventInfo.params.external_log=["/data/storage/el2/log/hiappevent/APP_CRASH_1502032265211_19237.log"] 302HiAppEvent eventInfo.params.log_over_limit=0 303``` 304 305### 移除并销毁事件观察者 306 3071. 移除事件观察者。 308 309 ```c++ 310 static napi_value RemoveWatcher(napi_env env, napi_callback_info info) { 311 // 使观察者停止监听事件 312 OH_HiAppEvent_RemoveWatcher(systemEventWatcher); 313 return {}; 314 } 315 ``` 316 3172. 销毁事件观察者。 318 319 ```c++ 320 static napi_value DestroyWatcher(napi_env env, napi_callback_info info) { 321 // 销毁创建的观察者,并置systemEventWatcher为nullptr。 322 OH_HiAppEvent_DestroyWatcher(systemEventWatcher); 323 systemEventWatcher = nullptr; 324 return {}; 325 } 326 ``` 327