• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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