基础库常见问题
解析大文件xml发生内存溢出(Out of Memory)
由于ArkTS侧提供的XML解析接口暂不支持流式解析模式,建议通过Native工程调用第三方C/C++库来实现。推荐使用libxml2库,该库具有成熟稳定、性能优越的特点,能够支持SAX等流式解析方式,有效降低内存占用。
具体实施步骤如下:
- 创建Native工程:在HarmonyOS项目中创建C++模块。
- 集成libxml2:下载并配置libxml2库源码或预编译库,在CMakeLists.txt中进行引用。
- 编写解析代码:使用libxml2提供的API实现流式解析逻辑。
- XML对象处理:当XML文件大小超过100MB时,建议在Native侧处理。
关于如何在ArkTS侧引用编译生成的三方so库,请参考文档:如何在ArkTS侧引用其他三方so库。
libxml2库支持的回调函数主要如下所示:
| 回调函数指针 | 触发时机 | 用途 |
|---|---|---|
| startDocument | 文档开始时 | 初始化环境,分配资源。 |
| endDocument | 文档结束时 | 释放资源,打印统计信息。 |
| startElement | 读到开始标签(如<tag>) | 获取标签名及其属性。 |
| endElement | 读到结束标签(如</tag>) | 处理标签结束逻辑,如出栈。 |
| characters | 读到标签间的文本内容 | 处理文本数据(注意可能被多次调用)。 |
代码示例:
// 用户自定义数据
ParseContext context;
// 初始化SAX Handler结构体
xmlSAXHandler SAXHandler = { 0 };
// 绑定回调函数,用于在解析过程中处理XML数据
SAXHandler.startDocument = startDocument;
SAXHandler.endDocument = endDocument;
SAXHandler.startElement = startElement;
SAXHandler.endElement = endElement;
SAXHandler.characters = characters;
// 解析文件
// 用户自定义数据指针
int ret = xmlSAXUserParseFile(&SAXHandler, &context, xmlFileName);
if (ret != 0) {
printf("Failed to parse XML file.\n");
return 1;
}
// 清理libxml2全局状态
xmlCleanupParser();
定时器被误删除
由于定时器ID为进程共享,是从0开始的,开发者误操作容易导致定时器被删除。
例如以下场景:
export class testClass {
// 初始值设置为0
private timeoutId: number = 0;
private intervalId: number = 0;
// 在某些情况下没有调用setTimeout设置定时器就调用了clearAnimation函数删除了定时器,就会导致timeoutId为0的定时器被删除
clearAnimation(): void {
clearInterval(this.intervalId);
clearTimeout(this.timeoutId);
}
}
可以通过以下方法快速定位:
重写globalThis.clearTimeout函数,实现在调用clearTimeout函数时打印调用栈,快速定位定时器是在哪里被删除的。
调用顺序为先调用clearTimeout.ts文件中的test()函数,再调用TimerTest.ets文件中testClass类的clearAnimation()函数。
示例代码:
// 自定义TS文件clearTimeout.ts
// test函数需要在程序调用clearTimeout函数之前调用
export function test() {
// 完全兼容原始 clearTimeout 类型
const origClear = globalThis.clearTimeout;
globalThis.clearTimeout = (...args: any[]) => {
const timeoutId = args[0];
// 检查所有可能的 timerId = 0 的情况
if (timeoutId === 0 || timeoutId === "0") {
console.info("清除 timerId = 0 !", new Error().stack);
// 触发断点
debugger;
}
// 使用 apply 确保正确传递所有参数
return origClear.apply(this, args);
}
}
// 自定义ets文件TimerTest.ets
export class testClass {
// 初始值设置为0
private timeoutId: number = 0;
private intervalId: number = 0;
// 在某些情况下没有调用setTimeout设置定时器就调用了clearAnimation函数删除了定时器,就会导致timeoutId为0的定时器被删除
clearAnimation(): void {
clearInterval(this.intervalId);
clearTimeout(this.timeoutId);
}
}
import { test } from './clearTimeout';
import { testClass } from './TimerTest';
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
test();
let testCase = new testClass();
testCase.clearAnimation();
this.message = 'success';
})
}
.width('100%')
}
.height('100%')
}
}