本原创文章帖发布在华为开发者联盟社区,欢迎开发者前往访问评论交流,更多与该内容相关讨论,请点击原帖查看:

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。

   • 转移依赖时,需同时转移dependenciesruntimeOnly依赖配置。

   • 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开发者

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐