1# 订阅主线程超时事件(ArkTS) 2 3## 主线程超时事件规格说明 4 5请参考[主线程超时事件介绍](./hiappevent-watcher-mainthreadjank-events.md)。 6 7## 接口说明 8 9API接口的具体使用说明(参数使用限制、具体取值范围等)请参考[应用事件打点API文档](../reference/apis-performance-analysis-kit/js-apis-hiviewdfx-hiappevent.md)。 10 11| 接口名 | 描述 | 12| --------------------------------------------------- | -------------------------------------------- | 13| addWatcher(watcher: Watcher): AppEventPackageHolder | 添加应用事件观察者,以添加对应用事件的订阅。 | 14| removeWatcher(watcher: Watcher): void | 移除应用事件观察者,以移除对应用事件的订阅。 | 15 16## 开发步骤 17 18以实现对发生**主线程采样栈**超时场景生成的主线程超时事件订阅为例,说明开发步骤。 19 201. 新建一个ArkTS应用工程,编辑工程中的“entry > src > main > ets > entryability > EntryAbility.ets”文件,导入依赖模块: 21 22 ```ts 23 import { hiAppEvent } from '@kit.PerformanceAnalysisKit'; 24 ``` 25 262. 编辑工程中的“entry > src > main > ets > entryability > EntryAbility.ets”文件,在onForeground函数中添加系统事件的订阅,示例代码如下: 27 28 ```ts 29 hiAppEvent.addWatcher({ 30 // 开发者可以自定义观察者名称,系统会使用名称来标识不同的观察者 31 name: "watcher", 32 // 开发者可以订阅感兴趣的系统事件,此处是订阅了主线程超时事件 33 appEventFilters: [ 34 { 35 domain: hiAppEvent.domain.OS, 36 names: [hiAppEvent.event.MAIN_THREAD_JANK] 37 } 38 ], 39 // 开发者可以自行实现订阅实时回调函数,以便对订阅获取到的事件数据进行自定义处理 40 onReceive: (domain: string, appEventGroups: Array<hiAppEvent.AppEventGroup>) => { 41 hilog.info(0x0000, 'testTag', `HiAppEvent onReceive: domain=${domain}`); 42 for (const eventGroup of appEventGroups) { 43 // 开发者可以根据事件集合中的事件名称区分不同的系统事件 44 hilog.info(0x0000, 'testTag', `HiAppEvent eventName=${eventGroup.name}`); 45 for (const eventInfo of eventGroup.appEventInfos) { 46 // 开发者可以对事件集合中的事件数据进行自定义处理,此处是将事件数据打印在日志中 47 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.domain=${eventInfo.domain}`); 48 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.name=${eventInfo.name}`); 49 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.eventType=${eventInfo.eventType}`); 50 // 开发者可以获取到主线程超时事件发生的时间戳 51 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.time=${eventInfo.params['time']}`); 52 // 开发者可以获取到主线程超时应用的版本信息 53 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.bundle_version=${eventInfo.params['bundle_version']}`); 54 // 开发者可以获取到主线程超时应用的包名 55 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.bundle_name=${eventInfo.params['bundle_name']}`); 56 // 开发者可以获取到主线程超时应用的pid、uid 57 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.pid=${eventInfo.params['pid']}`); 58 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.uid=${eventInfo.params['uid']}`); 59 // 开发者可以获取主线程处理开始和结束时间 60 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.begin_time=${eventInfo.params['begin_time']}`); 61 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.end_time=${eventInfo.params['end_time']}`); 62 // 开发者可以获取到主线程超时事件发生时的故障日志文件 63 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.external_log=${JSON.stringify(eventInfo.params['external_log'])}`); 64 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.log_over_limit=${eventInfo.params['log_over_limit']}`); 65 // 开发者可以获取到主线程超时事件时,任务执行的开始时间 66 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.app_start_jiffies_time=${JSON.stringify(eventInfo.params['app_start_jiffies_time'])}`); 67 // 开发者可以获取到生成的主线程超时日志文件中,打印最多次的调用栈 68 hilog.info(0x0000, 'testTag', `HiAppEvent eventInfo.params.heaviest_stack=${eventInfo.params['heaviest_stack']}`); 69 } 70 } 71 } 72 }); 73 ``` 74 753. (可选)该步骤用于模拟主线程超时事件。 76 编辑工程中的“entry > src > main > ets > pages> Index.ets”文件 77 78 ```ts 79 @Entry 80 @Component 81 struct Index { 82 build() { 83 RelativeContainer() { 84 Column() { 85 Button("timeOut350", { stateEffect:true, type: ButtonType.Capsule}) 86 .width('75%') 87 .height(50) 88 .margin(15) 89 .fontSize(20) 90 .fontWeight(FontWeight.Bold) 91 .onClick(() => { 92 let t = Date.now(); 93 while (Date.now() - t <= 350) {} 94 }) 95 }.width('100%') 96 } 97 .height('100%') 98 .width('100%') 99 } 100 } 101 ``` 102 1034. (可选)该步骤用于模拟自定义采样栈参数,并触发主线程超时事件场景。 104 105 编辑工程中的“entry > src > main > ets > pages> Index.ets”文件,本示例中设置一个customSample的Button控件,在onClick中实现自定义设置采样栈参数代码,示例代码如下: 106 107 ```ts 108 import { hiAppEvent, hilog } from '@kit.PerformanceAnalysisKit'; 109 import { BusinessError } from '@kit.BasicServicesKit'; 110 111 //模拟超时事件函数定义,示例代码: 112 function wait150ms() { 113 let t = Date.now(); 114 while (Date.now() - t <= 150){ 115 } 116 } 117 118 function wait500ms() { 119 let t = Date.now(); 120 while (Date.now() - t <= 500){ 121 } 122 } 123 124 @Entry 125 @Component 126 struct Index { 127 build() { 128 RelativeContainer() { 129 Column() { 130 //自定义设置采样栈参数按钮 131 Button("customSample", { stateEffect:true, type: ButtonType.Capsule}) 132 .width('75%') 133 .height(50) 134 .margin(15) 135 .fontSize(20) 136 .fontWeight(FontWeight.Bold) 137 .onClick(() => { 138 // 在按钮点击函数中进行事件打点,以记录按钮点击事件 139 let params: Record<string, hiAppEvent.ParamType> = { 140 // 事件类型定义, 0-默认值,1-只采样栈 2-只收集trace 141 "log_type": "1", 142 // 超时时间 & 采样间隔 143 "sample_interval": "100", 144 // 忽略启动开始时间 145 "ignore_startup_time": "11", 146 // 采样次数 147 "sample_count": "21", 148 // 事件上报次数定义 149 "report_times_per_app": "3" 150 }; 151 hiAppEvent.setEventConfig(hiAppEvent.event.MAIN_THREAD_JANK, params).then(() => { 152 hilog.info(0x0000, 'testTag', `HiAppEvent success to set event params.`) 153 }).catch((err: BusinessError) => { 154 hilog.error(0x0000, 'testTag', `HiAppEvent err.code: ${err.code}, err.message: ${err.message}`) 155 }); 156 }) 157 //触发150ms超时事件按钮 158 Button("timeOut150", { stateEffect:true, type: ButtonType.Capsule}) 159 .width('75%') 160 .height(50) 161 .margin(15) 162 .fontSize(20) 163 .fontWeight(FontWeight.Bold) 164 .onClick(() => { 165 wait150ms(); 166 }) 167 //触发500ms超时事件按钮 168 Button("timeOut500", { stateEffect:true, type: ButtonType.Capsule}) 169 .width('75%') 170 .height(50) 171 .margin(15) 172 .fontSize(20) 173 .fontWeight(FontWeight.Bold) 174 .onClick(() => { 175 wait500ms(); 176 }) 177 }.width('100%') 178 } 179 .height('100%') 180 .width('100%') 181 } 182 } 183 ``` 184 1855. 点击DevEco Studio界面中的运行按钮,运行应用工程,连续点击两次触发超时的按钮,会触发主线程超时事件。 186 1876. 主线程超时事件上报后,系统会回调应用的onReceive函数,可以在Log窗口看到对系统事件数据的处理日志: 188 189 主线程超时事件采样栈示例: 190 191 ```text 192 HiAppEvent eventInfo.domain=OS 193 HiAppEvent eventInfo.name=MAIN_THREAD_JANK 194 HiAppEvent eventInfo.eventType=1 195 HiAppEvent eventInfo.params.time=1717593620518 196 HiAppEvent eventInfo.params.bundle_version=1.0.0 197 HiAppEvent eventInfo.params.bundle_name=com.example.main_thread_jank 198 HiAppEvent eventInfo.params.pid=40986 199 HiAppEvent eventInfo.params.uid=20020150 200 HiAppEvent eventInfo.params.begin_time=1717593620016 201 HiAppEvent eventInfo.params.end_time=1717593620518 202 HiAppEvent eventInfo.params.external_log=["/data/storage/el2/log/watchdog/MAIN_THREAD_JANK_20240613211739_40986.txt"] 203 HiAppEvent eventInfo.params.log_over_limit=false 204 HiAppEvent eventInfo.params.app_start_jiffies_time=XXXX 205 HiAppEvent eventInfo.params.heaviest_stack=XXXX 206 ``` 207 208 主线程超时事件采样trace,与采样栈的结果大致相同,不同的地方: 209 210 ```text 211 栈: 212 采样栈增加两个参数:app_start_jiffies_time和heaviest_stack。 213 external_log=["/data/storage/el2/log/watchdog/MAIN_THREAD_JANK_yyyyMMDDHHmmss_xxxx.txt"]。xxxx:代表进程pid 214 215 trace: 216 external_log=[""/data/storage/el2/log/watchdog/MAIN_THREAD_JANK_unix时间戳_xxxx.trace"]。xxxx:代表进程pid 217 ``` 218