19 KiB
HarmonyOS APM分析报告
崩溃问题
1. 结论摘要
最新7日数据中(2026.7.24-2026.7.30):
| 数据源 | 七日数量 | 7月30日数量 | 当前统计口径 |
|---|---|---|---|
| 华为平台 | 12 | 6 | JS_ERROR、CPP_CRASH等崩溃事件 |
| Grafana | 33 | 17 | “崩溃类型 = All”,包含OOM |
两边的汇总事件类型未对齐。Grafana当前的“崩溃数”实际包含普通崩溃和OOM异常,而华为平台的“崩溃”统计不包含OOM异常。
核心结论:
- 主要差异由统计不一致造成。 Grafana将13次OOM计入“崩溃数”,华为平台则将OOM单独展示。
- 排除OOM后仍有2次差异。 华为平台包含3次CPP_CRASH,而Grafana非OOM明细中只有3条JS类异常和1条
unKnowException(已确认为CPP_CRASH)。 - 因此Grafana平台统计的崩溃率与实际值相比偏高,后续应将OOM异常排除崩溃统计,并入内存泄漏统计表盘。并进一步分析CPP_CRASH缺失原因。
2. 数据截图展示和分析
2.1 核对范围
| 维度 | 核对值 |
|---|---|
| 应用 | mobile-archive-am-qihuo-harmony |
| 应用版本 | FUHM037.08.390.10.32 / 1.20.02 / 10031 |
| 统计周期 | 2026-07-24至2026-07-30 |
| 重点核对日 | 2026-07-30 |
2.2 最新7日数据
2.2.1 华为平台最新7日数据
| 日期 | 华为平台崩溃次数 |
|---|---|
| 7月24日 | 0 |
| 7月25日 | 0 |
| 7月26日 | 0 |
| 7月27日 | 0 |
| 7月28日 | 3 |
| 7月29日 | 3 |
| 7月30日 | 6 |
| 合计 | 12 |
图1:华为平台最新7日数据。7月30日崩溃次数为6,七日合计12次。
2.2.2 Grafana最新7日数据
| 指标 | 数量 |
|---|---|
| 七日Grafana崩溃事件数 | 33 |
| 七日影响人数 | 32 |
| 7月30日Grafana崩溃事件数 | 17 |
图2:Grafana最新7日数据。该版本七日共33次,7月30日为17次。
2.2.3 七日汇总分析
Grafana 33次 = 崩溃(7次) + OOM(26次) + 可能的其他异常类型
华为平台 12次 = 崩溃事件
Grafana平台统计崩溃中大部分是OOM异常(26次),实际崩溃事件仅为(7次),且少于华为平台的12次崩溃。
2.3 华为平台7月30日数据
图3:华为平台7月30日崩溃次数为6。
图4:华为平台7月30日共6条问题,每条发生1次。
华为平台6条事件构成如下:
| 问题边界 | 类型 | 模块或位置摘要 | 次数 |
|---|---|---|---|
| SYSTEM | CPP_CRASH | /system/lib64/platformsdk/libuv.so |
1 |
| APP | JS_ERROR | MultiTypeInputRule.ts:22 / processInsert |
1 |
| APP | CPP_CRASH | libweituo_sdk_ohos.so |
1 |
| APP | JS_ERROR | MultiTypeInputRule.ts:22 / processInsert |
1 |
| SYSTEM | CPP_CRASH | libarkweb_engine.so |
1 |
| APP | JS_ERROR | NumberInputRule.ts:27 / shouldInsert |
1 |
类型汇总:
JS_ERROR 3次
CPP_CRASH 3次
合计 6次
2.4 Grafana 7月30日数据
图5:Grafana在7月30日共17次,影响17人。
Grafana使用“崩溃类型 = All”,下方明细把OOM与普通崩溃共同列入“对应版本崩溃列表”。
图6:Grafana明细上半部分,包含3条Cannot read、1条Default OOM及多条OutOfMemory。
图7:Grafana明细下半部分,包含其余OutOfMemory记录及1条unKnowException。
Grafana当日17条事件分类如下:
| Grafana描述类别 | 条数 | 是否计入华为平台“崩溃”页 |
|---|---|---|
Cannot read property index of undefined |
3 | 是,属于JS类崩溃 |
unKnowException |
1 | 是Native崩溃 |
Default oom error |
1 | 否,应归入OOM |
OutOfMemory when trying to allocate... |
12 | 否,应归入OOM |
| 合计 | 17 | — |
2.5 OOM明细及核算
| 序号 | OOM描述或申请字节数 | 次数 |
|---|---|---|
| 1 | Default oom error |
1 |
| 2 | OutOfMemory: 79,953,920 bytes |
1 |
| 3 | OutOfMemory: 84,148,224 bytes |
1 |
| 4 | OutOfMemory: 89,915,392 bytes |
1 |
| 5 | OutOfMemory: 99,352,576 bytes |
1 |
| 6 | OutOfMemory: 100,663,296 bytes |
1 |
| 7 | OutOfMemory: 100,925,440 bytes |
1 |
| 8 | OutOfMemory: 119,013,376 bytes |
1 |
| 9 | OutOfMemory: 120,324,096 bytes |
1 |
| 10 | OutOfMemory: 122,159,104 bytes |
1 |
| 11 | OutOfMemory: 139,722,752 bytes |
1 |
| 12 | OutOfMemory: 143,130,624 bytes |
1 |
| 13 | OutOfMemory: 169,869,312 bytes |
1 |
| 合计 | OOM事件 | 13 |
排除OOM后的核算结果:
Grafana可比崩溃数
= Grafana当日全口径事件数 - OOM事件数
= 17 - 13
= 4
2.6 当日对比结果
| 核对项 | 数量 |
|---|---|
| Grafana全口径事件数 | 17 |
| Grafana OOM | 13 |
| Grafana排除OOM | 4 |
| 华为平台崩溃 | 6 |
| 剩余差异 | 2 |
现有截图中的Grafana崩溃ID被截断,暂时无法与华为平台问题特征ID进行ID级关联。因此,13条OOM的分类结论明确;剩余2条差异的精确归属仍需ELK原始记录或完整详情确认。
3. 问题定性
3.1 统计事件类型并非完全是崩溃事件
Grafana统计“崩溃数量”,但却将OOM同时计入。
华为平台采用:
- “崩溃”页展示JS_ERROR、CPP_CRASH等崩溃事件。
- “OOM”页单独展示内存溢出事件。
因此,使用Grafana的崩溃总数与实际上相比偏大
该问题属于指标定义与界面表达不一致,容易让使用者误以为两边存在大规模数据缺失。
3.2 两次CPP/Native类事件未对齐
排除OOM后,两边类型构成对比:
| 类型 | 华为平台 | Grafana明细 | 备注 |
|---|---|---|---|
| JS类崩溃 | 3 | 3条Cannot read property... |
经过对比两平台数据内容一致 |
| CPP/Native类崩溃 | 3 | 1条unKnowException属于该类 |
华为平台的CPP_CRASH统计包含APP原因导致的崩溃,和系统原因导致的崩溃。 但是Grafana仅采集到了一条系统原因的CPP_CRASH,缺失另外两个CPP_CRASH |
| 合计 | 6 | 4 |
3条JS类事件基本可以对应。unKnowException对应其中1条Native事件,但仍有2条CPP_CRASH未在Grafana中以明确崩溃类型呈现。
3.3 最终定性
本次问题由两个层面组成:
- 指标统计问题: Grafana把OOM计入“崩溃数量”,导致汇总数明显偏高。
- 数据链路问题: 排除OOM后,仍有2条CPP/Native崩溃未对齐,存在漏报或者识别错误问题。
最终定性:
Grafana崩溃指标口径过宽,同时Native崩溃采集或展示存在漏报问题,需进一步分析。
4. 线上崩溃预防措施
4.1 Debug阶段崩溃诊断
在 Debug、测试包或内部环境中增加崩溃诊断弹窗,让开发者能在测试时及时发现异常并获取定位信息。该能力不面向正式用户开放。
- 进程级崩溃留存: JS Crash、CPP Crash 会导致当前进程退出,无法保证当场展示弹窗。因此应在应用下次启动后识别并读取上次异常退出的信息。
- 本地历史: 缓存最近 10 条崩溃记录,按最新优先展示。提供独立“崩溃记录”页面,列表展示摘要,点击单条后再查看完整信息。
- 启动提示策略: 应用启动后自动提示最新一条崩溃。“确认”只关闭本次弹窗,下次启动仍提示;“暂不处理”后续不再提示当前崩溃,直到出现新的崩溃记录。
- 诊断操作: 支持清除本地历史,并将崩溃记录导出为压缩包保存到手机,便于附在缺陷单中。
该能力只在 Debug测试包或内部环境启用;正式包仍应使用线上崩溃平台完成聚合、告警和版本追踪。
启动耗时
1. 现状
目前与其他端相比,鸿蒙端APM缺失启动耗时的统计与上报功能,需要上线该功能来监控和分析启动耗时问题。
2. 可行性分析
2.1 获取应用开始启动的时间
在鸿蒙开发中,要监听用户手指点击应用图标开始启动的时间,可以通过系统提供的启动耗时事件来获取。系统会在用户点击图标时自动打点记录,开发者通过订阅 HiAppEvent 的 APP_LAUNCH 事件,即可在参数 icon_input_time 中拿到这个精确的时间戳。
示例代码
import { hiAppEvent, hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 订阅应用启动耗时事件
hiAppEvent.addWatcher({
name: "appStartupWatcher",
// 订阅应用启动事件
events: ["APP_LAUNCH"],
onReceive: (domain: string, events: Array<hiAppEvent.AppEventPackage>) => {
for (const event of events) {
// 获取事件携带的参数
const params = event.params as Record<string, Object>;
// icon_input_time:用户点击桌面图标离手的时间戳(毫秒)
const iconInputTime = params['icon_input_time'] as number;
// extend_time:开发者调用 reportDrawnCompleted 的时间戳
const extendTime = params['extend_time'] as number;
// ......
}
}
});
注意,上述参数中的extend_time时间是根据开发者自定义调用reportDrawnCompleted时的时间戳,会在下述章节中阐述调用时机。
2.2 获取应用首屏渲染完毕的时间
要判断首页渲染完毕通常指首帧内容完整绘制到屏幕的时刻。可以通过 @kit.ArkUI.inspector 的 createComponentObserver 接口,监听首页根组件的 draw 事件,在回调中记录时间戳,即为渲染完成时刻。
示例代码:
import { inspector } from '@kit.ArkUI';
@Entry
@Component
struct HomePage {
@State startTime: number = 0;
private listener: inspector.ComponentObserver =
this.getUIContext().getUIInspector().createComponentObserver('HOME_ROOT');
private onDrawComplete = () => {
const renderEndTime = new Date().getTime();
console.info('首页渲染完成时间戳:', renderEndTime);
// 可在此处计算启动耗时:renderEndTime - icon_input_time
};
aboutToAppear() {
this.startTime = new Date().getTime();
this.listener.on('draw', this.onDrawComplete);
}
aboutToDisappear() {
this.listener.off('draw', this.onDrawComplete);
}
build() {
Column() {
// 首页核心内容
}
.id('HOME_ROOT') // 必须与 createComponentObserver 的 id 一致
.width('100%')
.height('100%')
}
}
3. 开发方案
3.1 首屏渲染
根据2.2可行性分析章节,可在应用的首页IndexM页面中监听整个页面的根组件:
struct IndexM {
build() {
Flex(...) { ... }
.id('HOME_ROOT')
}
}
在页面的aboutToAppear()的末尾使用inspector注册监听根组件HOME_ROOT的绘制完成:
// 创建首页根组件观察器,用于监听 HOME_ROOT 的绘制事件
private drawnListener = this.getUIContext().getUIInspector().createComponentObserver('HOME_ROOT');
// 页面绘制完成后通知系统,以便记录首帧渲染完成时间
private onDrawComplete = () => {
// draw 事件可能因页面刷新重复触发,上报前先取消监听
this.drawnListener.off('draw', this.onDrawComplete);
this.context.reportDrawnCompleted(() => {});
};
aboutToAppear() {
// ...
// 页面即将显示时注册绘制监听;根组件完成绘制后触发回调
this.drawnListener.on('draw', this.onDrawComplete);
}
aboutToDisappear() {
// 页面退出时兜底取消监听,避免观察器残留
this.drawnListener.off('draw', this.onDrawComplete);
}
整体步骤:
- 为页面根组件添加id
HOME_ROOT; - 首页组件获取系统绘制完成
inspector监听对象,用于监听HOME_ROOT的绘制完成事件draw; draw事件回调中调用reportDrawnCompleted函数,用于在HiAppEvent监听的APP_LAUNCH事件函数中获取渲染完毕的时间戳;- 在项目已有的事件监听架构中,独立添加
APP_LAUNCH事件的监听,并根据2.1可行性分析中获取到的icon_input_time启动时间和extend_time首页绘制完成时间来计算应用启动耗时; - 复用已有的事件上传方法上传启动时间。
4. 时间校验
同一次冷启动中,应用日志与性能时间线的首帧耗时结果基本一致:日志记录为 1270 ms,性能时间线显示为约 1.2 s。说明 APP_LAUNCH 事件中的首屏绘制完成时间可用于启动耗时统计。
图8:首屏绘制完成耗时为 1270 ms。
图9:性能时间线中 First Frame - App Phase 显示首帧耗时约 1.2 s,与应用日志结果一致。
另外补充首帧效果图:
**图10:首页首帧。
5. 启动性能劣化预防
冷启动自动化回归可分三个阶段执行:
- 开发自测阶段: 首页初始化、SDK、路由或资源加载有改动时,开发者在本机或固定测试机进行少量冷启动采样,尽早发现明显劣化。
- 合并主干阶段: 每次合入主干后由 CI 在固定真机执行冷启动回归,作为日常预警并持续积累稳定基线。
- 发布候选阶段: 提测包、灰度包或正式发布包执行完整的多次采样和门禁判断;P95 明显劣化、启动失败或白屏时阻断发布。
发布候选阶段适合作为正式质量门禁;合并主干阶段是最有价值的预防点,因为问题仍容易定位和回滚。
5.1 开发自测:work 与 feature 分支对比
开发阶段可约定 feature 为稳定基线分支,work 为当前编码分支。通过脚本自动构建、安装并测试两个分支的冷启动耗时,输出首帧耗时及其差值;若 work 明显慢于 feature,开发者应在提交前检查新增初始化、资源加载或首页渲染逻辑。
为避免波动的影响,两个分支应在同一台真机、相同网络条件下交替执行多次(比如10次)冷启动,可使用平均数进行比较,并输出性能测试对比文档,该脚本用于开发阶段性能劣化预警。
5.2 feature合并主干或者主干发布前
合并主干后,CI 可在在固定真机上执行冷启动回归,持续对比当前版本与稳定基线的首帧耗时,作为日常预警。发现明显劣化时,开发者可问题进入发布流程前完成分析和修复。
正式发布前,应扩大采样次数,重点比较判断整体启动体验是否变慢,若明显劣化,或出现启动失败等问题,可暂停合并并保留测试日志和性能证据用于定位和修复。
APP_FREEZE
1. 现状
当前 APP_FREEZE 已具备捕获和上传能力。
2. 数据分析
版本号: FUHM37.08.390 / 1.20.02 时间范围: 2026-07-24 ~ 2026-07-30
| 平台 | 数量 |
|---|---|
| 华为 | 100 |
| Grafnan | 97 |
3. 目标
按数量排名,优先定位和治理排名靠前的10个 APP_FREEZE 异常。
改进目标与方向
改进目标
| 优先级 | 事项 | 具体措施 | 现状 | 验收标准 | 预计时间 |
|---|---|---|---|---|---|
| P0 | 启动耗时 | 监听应用启动事件获取用户点击桌面事件和应用首屏渲染结束时间,计算出启动耗时。上传按照其他端后端上传方案。 | 期货鸿蒙缺失该功能 | 可准确获取应用启动耗时并上报统计。 | 4d(编码+自测+验收) |
| P0 | app freeze(ANR) | 目前APP_FREEZE已有捕获上传,但基本未治理。可先选取量级排名前10的先治理 | FUHM37.08.390(1.20.02)版本冻屏幕率: 7.7 (日/万) |
预计占比修复后降低到: 4(日/万) |
4d(定位+修复) |
| P0 | 崩溃 | 目前的崩溃事件中的存在大量OOM事件,占比超过一半。需对OOM单独治理。 | FUHM37.08.390(1.20.02)崩溃率: 5.08 (日/万) |
目前OOM较难定位,还需继续定位问题。 | 1d定位 |
| P1 | 崩溃开发阶段预防措施 | 在 Debug阶段增加崩溃诊断弹窗,在崩溃应用退出后下一次启动弹出,帮助开发者和测试在开发测试阶段发现和定位崩溃信息。 | |||
| P1 | 启动性能劣化预防措施 | 编写自动化性能测试脚本,自动测试、获取和对比待合并和目标分支的启动性能。遇到性能劣化的情况可以及时修复。 | |||
后续方向
- 资源泄漏:问华为
- 网络相关。定位并降低HTTP请求慢率等指标。定位并优化WEB白屏和慢加载等问题。
- 待完善......






