上一篇讲了功耗检测的原理和门限,本文继续介绍在 ArkTS 工程中订阅 CPU 高负载事件。

一、核心接口

接口名描述
addWatcher(watcher: Watcher): AppEventPackageHolder添加应用事件观察者,以添加对应用事件的订阅
removeWatcher(watcher: Watcher): void移除应用事件观察者,以移除对应用事件的订阅

二、开发步骤(以多线程死循环触发CPU高负载为例)

步骤1:导入依赖模块

编辑 entry > src > main > ets > entryability > EntryAbility.ets,导入依赖模块:

import { hiAppEvent, hilog } from '@kit.PerformanceAnalysisKit';

步骤2:添加系统事件订阅

在 onCreate 函数中添加订阅,示例代码:

hiAppEvent.addWatcher({
  // 可以自定义观察者名称,系统会使用名称来标识不同的观察者
  name: "watcher",
  // 可以订阅感兴趣的系统事件,此处是订阅了CPU高负载事件
  appEventFilters: [
    {
      domain: hiAppEvent.domain.OS,
      names: [hiAppEvent.event.CPU_USAGE_HIGH]
    }
  ],
  // 可以自行实现订阅实时回调函数,以便对订阅获取到的事件数据进行自定义处理
  onReceive: (domain: string, appEventGroups: Array<hiAppEvent.AppEventGroup>) => {
    hilog.info(0x0000, 'testTag', 'HiAppEvent onReceive: domain=%{public}s', domain);
    for (const eventGroup of appEventGroups) {
      // 可以根据事件集合中的事件名称区分不同的系统事件
      hilog.info(0x0000, 'testTag', 'HiAppEvent eventName=%{public}s', eventGroup.name);
      for (const eventInfo of eventGroup.appEventInfos) {
        // 可以对事件集合中的事件数据进行自定义处理,此处是将事件数据打印在日志中
        hilog.info(0x0000, 'testTag', 'HiAppEvent eventInfo=%{public}s', JSON.stringify(eventInfo));
      }
    }
  }
});

步骤3(可选):自定义CPU高负载事件配置策略

示例代码:

import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

let policy: hiAppEvent.EventPolicy = {
  "cpuUsageHighPolicy": {
    "foregroundLoadThreshold": 10,  // 设置应用前台CPU负载异常阈值为 10%
    "backgroundLoadThreshold": 5,   // 设置应用后台CPU负载异常阈值为 5%
    "threadLoadThreshold": 50,      // 设置应用线程CPU负载异常阈值为 50%
    "perfLogCaptureCount": 3,       // 设置采样栈每日采集次数上限为3次
    "threadLoadInterval": 30        // 设置应用线程负载异常检测周期为30秒
  }
};

hiAppEvent.configEventPolicy(policy).then(() => {
  hilog.info(0x0000, 'hiAppEvent', 'Successfully set cpu usage high event policy.');
}).catch((err: BusinessError) => {
  hilog.error(0x0000, 'hiAppEvent', 'Failed to set cpu usage high event policy. Code: %{public}d, message: %{public}s', err?.code, err?.message);
});

说明:抓取采样栈有内存开销,影响功耗性能,只建议在本地调试时使用自定义参数功能。

采样栈每日采集次数阈值范围:

应用版本阈值范围
Debug 版本[-1, 100]
Release 版本[0, 20]

步骤4:添加 Worker 死循环

在 entry > src > main > ets > workers > worker.ets 中构造死循环,接收到主线程消息后触发 CPU 高负载:

import { worker } from '@kit.ArkTS';

let workerPort = worker.workerPort;

workerPort.onmessage = (message) => {
  eatCpu();
};

function eatCpu(): void {
  let val: number = 0;
  while (true) {
    val++;
  }
}

步骤5:添加 CpuTest 类

在 entry > src > main > ets > test > CpuTest.ets 中开启多个线程的死循环:

import { worker } from '@kit.ArkTS';

export default class CpuTest {
  workerInstance: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/worker.ets');

  start(threadNum: number) {
    for (let index = 0; index < threadNum; index++) {
      this.workerInstance = new worker.ThreadWorker('entry/ets/workers/worker.ets');
      this.workerInstance.postMessage('msg');
    }
  }
}

步骤6:在页面添加"CPU加压"按钮

在 entry > src > main > ets > pages > Index.ets 中添加按钮并触发多线程死循环:

import CpuTest from './test/CpuTest';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';
  @State enable: boolean = true;
  @State threadNum: number = 6;
  cpuTest: CpuTest = new CpuTest();

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        Button('CPU 加压')
          .fontSize(18)
          .margin(12)
          .fontWeight(FontWeight.Bold)
          .enabled(this.enable)
          .onClick(() => {
            this.cpuTest.start(this.threadNum);
            this.enable = false;
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

步骤7:运行并验证

  1. 点击 DevEco Studio 的运行按钮,运行应用

  2. 在应用界面点击 "CPU 加压" 按钮,触发 CPU 高负载事件

  3. 应用保持在前台且屏幕处于亮屏状态,五到十分钟之后可以在 Log 窗口看到事件处理日志

说明:

  • 默认状态下,CPU 高负载事件抓取次数存在限制

  • 若未抓取到 external_log 日志,可以尝试重启测试设备重新抓取或自定义相关配置策略

日志示例

HiAppEvent eventInfo={
  "domain":"OS",
  "eventType":1,
  "name":"CPU_USAGE_HIGH",
  "params":{
    "begin_time":1723725531352,
    "bundle_name":"com.xxx.test",
    "bundle_version":"1.0.0",
    "end_time":1623625863613,
    "external_log":[
      "data/storage/el2/log/hiappevent/CPU_USAGE_HIGH_1623625950016_0.log",
      "data/storage/el2/log/hiappevent/CPU_USAGE_HIGH_16232625950196_0.log"
    ],
    "fault_type":1,
    "foreground":false,
    "log_over_limit":false,
    "threads":[
      {"name":"com.xxx.test","tid":60677,"usage":0.0708},
      {"name":"WorkerThread","tid":60856,"usage":7.5353},
      {"name":"WorkerThread","tid":60855,"usage":7.5645},
      {"name":"WorkerThread","tid":60854,"usage":7.5120},
      {"name":"WorkerThread","tid":60853,"usage":7.5660}
    ],
    "time":1723725949836,
    "usage":25
  }
}

自定义策略参数

参数含义示例值
foregroundLoadThreshold前台 CPU 负载异常阈值10(%)
backgroundLoadThreshold后台 CPU 负载异常阈值5(%)
threadLoadThreshold线程 CPU 负载异常阈值50(%)
perfLogCaptureCount采样栈每日采集次数上限3
threadLoadInterval线程负载异常检测周期30(秒)

如果应用被反馈耗电异常,可以先用这套流程在测试环境复现,拿到 external_log 后分析调用栈和采样栈,定位具体是哪段代码导致的 CPU 高负载。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐