HarmonyOS开发常见问题及解答(二)
本原创文章帖发布在华为开发者联盟社区,欢迎开发者前往访问评论交流,更多与该内容相关讨论,请点击原帖查看:
HarmonyOS开发常见问题及解答(二)-华为开发者话题 | 华为开发者联盟
FAQ 1:HarmonyOS imageSource.getImageProperty接口内存泄漏问题如何分析?
【解决方案】:
【问题定位】
一、ImageSource/PixelMap对象未释放:
调用createPixelMap后未主动调用release()释放资源,导致底层共享内存(如/dev/ashmem/EXT RawData)未回收。
多次调用getImageProperty可能累积未释放的临时对象。
二、Exif数据解析残留:
解析大尺寸图片的Exif信息时,若未正确处理旋转后的PixelMap缓存,可能占用ION内存。
三、异步操作未终止:
网络图片下载过程中组件销毁,但异步任务仍持有对Image组件的强引用。
【修改建议】
1、显式释放资源
import image from '@kit.ImageKit';
async function loadImage() {
let imageSource: image.ImageSource | null = null;
try {
imageSource = image.createImageSource(filePath);
const pixelMap = await imageSource.createPixelMap();
// 使用pixelMap后显式释放
pixelMap.release();
} catch (err) {
console.error("Error:", err);
} finally {
// 释放ImageSource
imageSource?.release();
}
}
2、组件生命周期管理
import { image } from '@kit.ImageKit';
@Entry
@Component
struct ImageComponent {
@State pixelMap: image.PixelMap | null = null;
aboutToDisappear() {
this.pixelMap?.release(); // 组件销毁前释放资源
}
build() {
Column() {
Image(this.pixelMap)
.onClick(async () => {
const src = image.createImageSource(networkUrl);
this.pixelMap = await src.createPixelMap();
})
}
}
}
3、异步任务管理
import { taskpool } from '@kit.ArkTS';
// 使用TaskPool避免持有组件引用
taskpool.execute(async () => {
const src = image.createImageSource(filePath);
const orientation = src.getImageProperty('Orientation');
src.release();
return orientation;
});
【典型错误场景】:
案例:未关闭文件流导致ION泄漏
// 错误示例:未释放ImageSource
imageSource.createPixelMap().then(pixelMap => {
this.image = pixelMap;
});
// 正确修正:在Promise链中释放
imageSource.createPixelMap().then(pixelMap => {
this.image = pixelMap;
imageSource.release(); // 显式释放
});
FAQ 2:har文件间相互依赖导致运行时模块导入错误,无法正确使用导出的代码,导致运行崩溃,如何分析?
【解决方案】:
HAR模块间动态import依赖解耦
当应用包含多个HAR包,HAR包之间的依赖关系比较复杂。在DevEco Studio中配置依赖关系时,可能会形成循环依赖。这时,如果HAR之间的依赖关系中仅有变量动态import,可以将HAR包之间直接依赖关系转移到HAP/HSP中配置,HAR包之间无需配置依赖关系,从而达到HAR包间依赖解耦的目的。如下示意图:

HAR之间的依赖关系转移至HAP/HSP后:

使用限制
• 仅限在本地源码HAR包之间存在循环依赖时,使用该规避方案。
• 被转移依赖的HAR之间只能通过变量动态import,不能有静态import或常量动态import。
• 转移依赖时,需同时转移dependencies和runtimeOnly依赖配置。
• HSP不支持转移依赖。即:HAP->HSP1->HSP2->HSP3,这里的HSP2和HSP3不能转移到HAP上面。
• 转移依赖的整个链路上只能有HAR包,不能跨越HSP转移。即:HAP->HAR1->HAR2->HSP->HAR3->HAR4,HAR1对HAR2的依赖可以转移到HAP上,HAR3对HAR4的依赖可以转移到HSP上。但是,不能将HAR3或HAR4转移到HAP上。
• 如果引用了其他工程模块、远程包或集成HSP,需确保在工程级build-profile.json5文件中的useNormalizedOHMUrl配置一致,同时设置为true或false,否则可能导致运行错误:Cannot find dynamic-import module library。
FAQ 3:通过resourceManager.getStringResource接口获取HSP资源文件报“Resource id invalid”错误 如何解决?
【详细场景】:
通过this.resourceManager.getStringResource($r('app.string.PlayCount').id)获取HSP资源文件时出现错误。
错误消息:资源ID无效。
错误代码:9001001
SourceCode:returnResource = this.context.resourceManager.getStringSync(id)。
【解决方案】:
根据模块名创建上下文Context,然后使用getStringByNameSync方法获取指定资源名称对应的字符串。具体示例代码如下:
import { common, application } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { JSON } from '@kit.ArkTS';
@Entry
@Component
struct Index {
private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
build() {
Column() {
Button()
.onClick(() => {
// Create a context based on the module name
let moduleName: string = 'library';
application.createModuleContext(this.context, moduleName)
.then((data: common.Context) => {
console.info(`CreateModuleContext success, data: ${JSON.stringify(data)}`);
if (data !== null) {
this.getUIContext().getPromptAction().showToast({
message: ('get Context success')
});
}
// Then run getStringByNameSync to obtain the string corresponding to the specified resource name
try {
let str = data.resourceManager.getStringByNameSync('shared_desc');
console.info(`getStringByNameSync, data: ${JSON.stringify(str)}`);
} catch (error) {
let code = (error as BusinessError).code;
let message = (error as BusinessError).message;
console.error(`getStringByNameSync failed, error code: ${code}, message: ${message}.`);
}
})
.catch((err: BusinessError) => {
console.error(`CreateModuleContext failed, err code:${err.code}, err msg: ${err.message}`);
});
})
}
}
}
参考链接
应用上下文Context 、获取本应用中其他module的context 、getStringByNameSync
FAQ 4:对于数据量大的赋值操作造成的卡顿和崩溃,该怎么解决?
【解决方案】:
数据精准懒加载:
当数据源总长度较长,或数据项加载耗时较长时,可使用Repeat数据精准懒加载特性,避免在初始化时加载所有数据。Repeat数据精准懒加载特性从API version 19开始支持。
开发者可以设置.virtualScroll()的totalCount属性值或onTotalCount自定义方法用于计算期望加载的数据项总数,设置onLazyLoading属性实现数据精准懒加载,实现在节点首次渲染时加载对应的数据。详细说明和注意事项见VirtualScrollOptions 。
示例
数据源总长度较长,在首次渲染、滑动屏幕、跳转显示区域时,动态加载对应区域内的数据。
@Entry
@ComponentV2
struct RepeatLazyLoadingLongData {
// 假设数据源总长度较长,为1000。初始数组未提供数据。
@Local arr: Array<string> = [];
scroller: Scroller = new Scroller();
build() {
Column({ space: 5 }) {
// 初始显示位置为index = 100,数据可通过懒加载自动获取。
List({ scroller: this.scroller, space: 5, initialIndex: 100 }) {
Repeat(this.arr)
.virtualScroll({
// 期望的数据源总长度为1000。
onTotalCount: () => {
return 1000;
},
// 实现数据懒加载。
onLazyLoading: (index: number) => {
this.arr[index] = index.toString();
}
})
.each((obj: RepeatItem<string>) => {
ListItem() {
Row({ space: 5 }) {
Text(`${obj.index}: Item_${obj.item}`)
}
}
.height(50)
})
}
.height('80%')
.border({ width: 1 })
// 显示位置跳转至index = 500,数据可通过懒加载自动获取。
Button('ScrollToIndex 500')
.onClick(() => {
this.scroller.scrollToIndex(500);
})
}
}
}
更多实例及运行效果详见:Repeat:可复用的循环渲染-学习UI范式渲染控制-UI开发 (ArkTS声明式开发范式)-ArkUI(方舟UI框架)-应用框架 - 华为HarmonyOS开发者
FAQ 5:使用向导创建的worker线程文件中导出一个TestThrdMsg类必现JSCrash,如何解决?
【解决方案】:
Worker简介的其它注意事项中有说明:在Worker文件中禁止使用export语法导出任何内容,否则会导致jscrash问题。
使用过程中可参考Worker简介:Worker简介-多线程并发-ArkTS并发-ArkTS(方舟编程语言)-应用框架 - 华为HarmonyOS开发者
更多推荐

所有评论(0)