你是不是也在想——“鸿蒙这么火我能不能学会”答案是当然可以这个专栏专为零基础小白设计不需要编程基础也不需要懂原理、背术语。我们会用最通俗易懂的语言、最贴近生活的案例手把手带你从安装开发工具开始一步步学会开发自己的鸿蒙应用。不管你是学生、上班族、打算转行还是单纯对技术感兴趣只要你愿意花一点时间就能在这里搞懂鸿蒙开发并做出属于自己的App关注本专栏《零基础学鸿蒙开发》一起变强每一节内容我都会持续更新配图代码解释全都有欢迎点个关注不走丢我是小白酷爱学习我们一起上路 全文目录前言一、onMemoryLevel 解决的不是“查剩余内存”二、先把官方规则和版本变化弄清楚三、搭一个最小实践把“可重建缓存”分成三层四、在 AbilityStage 中处理内存等级五、别漏掉 module.json5 的 AbilityStage 入口六、哪些缓存适合释放哪些数据不能跟着清七、还有两个容易误解的地方1. 收到回调不等于知道自己释放了多少内存2. onMemoryLevel 不是应用内存泄漏的修复器八、实际项目可以按这个顺序排查开发经验总结前言应用里的缓存通常是为了用内存换取速度预取结果、可重新计算的数据、临时索引都可能长期留在进程里。但系统整体内存开始紧张时应用如果还按正常状态持有所有非必要资源就失去了主动收缩的机会。HarmonyOS 的 Ability Kit 提供了onMemoryLevel()。以AbilityStage为例当系统决定调整内存时会把内存压力级别回调给应用应用可以据此释放不再必要、并且能够重新构建的资源。官方文档明确把AbilityStage、UIAbility和EnvironmentCallback都列为接收内存变化的方式。这篇文章只做一件事围绕AbilityStage.onMemoryLevel()搭一个最小实践把“收到通知之后到底应该清什么、不应该清什么”说明白。一、onMemoryLevel 解决的不是“查剩余内存”先把这个接口的定位弄清楚。onMemoryLevel()不是一个查询“当前还剩多少 MB 内存”的接口。它接收到的是AbilityConstant.MemoryLevel表达系统当前的内存压力等级。AbilityStage 官方开发指南说明当系统调整内存时会触发onMemoryLevel()开发者可以在回调中释放不必要资源。因此它更适合解决这样的业务问题应用维护了若干用于加速访问的内存缓存。当系统出现内存压力时根据压力等级逐步丢弃可以重新获得的数据同时保留用户正在编辑、尚未持久化或业务流程仍然依赖的关键状态。这里的核心不是“系统帮应用清缓存”而是系统给出压力信号具体释放什么仍由应用自己的资源管理策略决定。这也是为什么onMemoryLevel()最适合与缓存分级一起设计而不是收到回调之后简单粗暴地执行一次“全清”。二、先把官方规则和版本变化弄清楚本文以当前 HarmonyOS 7 开发环境为背景。华为官方的 26.0.0 升级适配说明明确指出HarmonyOS 7.0 对应 API 26.0.0并建议开发者基于 26.0.0 开发套件完成升级适配。AbilityStage 属于 Stage 模型中的模块级组件容器。一个 AbilityStage 实例对应一个模块默认工程不会自动生成 AbilityStage需要使用时可以自行创建并通过module.json5的srcEntry指定入口。官方指南同时明确列出了onMemoryLevel()这个事件回调。对于本文最关心的三个基础内存等级华为官方内存分析最佳实践给出的定义如下。需要说明的是该最佳实践页面目前属于 HarmonyOS 5.0.0(API 12) 的归档文档因此这里使用它核对三个既有枚举的含义而不是把该页面当成 HarmonyOS 7 的完整枚举清单。MemoryLevel值应用侧应理解为什么MEMORY_LEVEL_MODERATE0系统内存压力适中系统可能开始按照 LRU 缓存规则处理进程MEMORY_LEVEL_LOW1系统内存已经比较低应释放不必要资源MEMORY_LEVEL_CRITICAL2系统内存很低应尽可能释放不必要资源这里还有一个 HarmonyOS 7 开发时不能忽略的版本点不要再把MemoryLevel永久写死为“只有 0、1、2 三种值”。官方 6.1.1(24) Beta1 的 Ability Kit API 变更记录已经新增MEMORY_LEVEL_UI_HIDDEN 3、MEMORY_LEVEL_BACKGROUND_MODERATE 4、MEMORY_LEVEL_BACKGROUND_LOW 5、MEMORY_LEVEL_BACKGROUND_CRITICAL 6。所以本文虽然围绕题目中的MODERATE / LOW / CRITICAL三档讲最小策略但面向 API 26 编写代码时switch最好保留兜底分支而不是假设枚举永远只有三个成员。从官方 AbilityStage 示例来看核心依赖来自 Ability Kitimport{AbilityStage,AbilityConstant}fromkit.AbilityKit;本文使用的onMemoryLevel()没有额外申请运行时权限AbilityStage 的接入重点在 Stage 模型、Ability Kit 以及模块srcEntry配置而不是权限声明。三、搭一个最小实践把“可重建缓存”分成三层示例不接入图片解码、数据库、网络请求等额外能力避免把文章变成另一个主题。我们只准备三个Map模拟三类可以重新获得的数据prefetchCache预取数据优先级最低derivedCache根据原始业务数据计算得到的派生结果可以再次计算optionalCache仍然属于非必要缓存但正常情况下希望尽量保留。注意这三个Map只是为了展示释放策略和代码结构。本文没有测量它们能够释放多少真实内存也不会据此声称任何具体内存收益。classDemoMemoryCache{privateprefetchCache:Mapstring,stringnewMap();privatederivedCache:Mapstring,stringnewMap();privateoptionalCache:Mapstring,stringnewMap();releasePrefetchCache():void{this.prefetchCache.clear();}releaseRecreatableCache():void{this.prefetchCache.clear();this.derivedCache.clear();}releaseAllNonEssentialCache():void{this.prefetchCache.clear();this.derivedCache.clear();this.optionalCache.clear();}}exportconstdemoMemoryCachenewDemoMemoryCache();真正迁移到业务工程时这三个容器可以对应自己的图片内存缓存、预取结果、计算结果或其他可重建对象。关键判断标准不是“它占了多少内存”而是丢掉以后应用能不能在需要时重新得到它并且不会造成用户数据丢失。四、在 AbilityStage 中处理内存等级AbilityStage 默认不会自动出现在普通工程中。按照官方指南可以新建例如entry/src/main/ets/myabilitystage/MyAbilityStage.ets然后实现onMemoryLevel()import{AbilityStage,AbilityConstant}fromkit.AbilityKit;import{demoMemoryCache}from../common/DemoMemoryCache;exportdefaultclassMyAbilityStageextendsAbilityStage{onMemoryLevel(level:AbilityConstant.MemoryLevel):void{console.info(onMemoryLevel:${level});switch(level){caseAbilityConstant.MemoryLevel.MEMORY_LEVEL_MODERATE:// 压力适中先放弃低价值的预取缓存demoMemoryCache.releasePrefetchCache();break;caseAbilityConstant.MemoryLevel.MEMORY_LEVEL_LOW:// 内存较低进一步释放可重新生成的数据demoMemoryCache.releaseRecreatableCache();break;caseAbilityConstant.MemoryLevel.MEMORY_LEVEL_CRITICAL:// 内存非常紧张释放全部非必要缓存demoMemoryCache.releaseAllNonEssentialCache();break;default:// 新版本可能扩展 MemoryLevel避免把枚举永久写死为三个值console.info(Unhandled memory level:${level});break;}}}这段代码解决的问题很明确把系统内存压力和应用自己的缓存回收策略连接起来。真正需要关注的是三个地方。第一不要比较魔法数字0 / 1 / 2直接使用AbilityConstant.MemoryLevel中的枚举成员。这样代码表达的是“内存压力等级”而不是几个没有语义的整数。第二三个等级不必执行完全相同的操作。如果MODERATE一来就把所有缓存清空那么分级通知本身的价值就没有发挥出来。可以让释放策略随压力逐步增强。第三保留default。官方 API 变更记录已经证明MemoryLevel后续确实发生过扩展。五、别漏掉 module.json5 的 AbilityStage 入口如果工程原本没有 AbilityStage仅仅创建.ets文件还不够。官方 AbilityStage 指南要求在module.json5中通过srcEntry指定模块入口例如{ module: { name: entry, type: entry, srcEntry: ./ets/myabilitystage/MyAbilityStage.ets } }实际项目的module.json5通常还有abilities、deviceTypes等其他配置这里只展示和 AbilityStage 接入直接相关的字段不建议拿这段最小片段覆盖已有配置。六、哪些缓存适合释放哪些数据不能跟着清onMemoryLevel()最容易出现的理解偏差是把“释放不必要资源”理解成“清掉当前进程里能找到的所有数据”。两者完全不是一回事。比较适合进入内存回收策略的是能够重新获取、重新加载或重新计算的非必要数据。例如预取结果、派生计算结果以及应用自己明确设计为可重建的内存缓存。而下面这类内容不应该仅仅因为收到onMemoryLevel()就直接丢弃用户尚未保存的编辑内容、仍在进行中的关键业务状态、唯一存在于内存中且没有其他可靠副本的数据。可以把判断方式简化成一句话Cache 可以考虑释放Source of Truth 不能被当成 Cache。比如文章编辑器里已经从服务端加载完成的缩略图缓存和用户刚刚输入但还没有保存的正文虽然都可能暂存在内存里业务意义完全不同。前者丢失后通常可以重新获取后者如果直接清除就可能造成真实的数据损失。因此CRITICAL的含义也不是“允许删除业务数据”而是应该把非必要资源的释放力度提高。官方对该等级的表述同样限定在“尽可能释放任何不必要的资源”。七、还有两个容易误解的地方1. 收到回调不等于知道自己释放了多少内存onMemoryLevel(level)给应用的是等级不是“请释放 120 MB”这样的目标值。所以不要在文章、日志或性能报告里把“执行了Map.clear()”直接写成“释放了 XX MB”。如果项目需要量化内存变化应该单独使用内存分析工具建立测试方法而不是从回调等级推算结果。华为归档的内存分析最佳实践还给出了通过 HiDumper 查看 PSS以及使用 DevEco Profiler 进行 Allocation、Snapshot 分析的思路。2. onMemoryLevel 不是应用内存泄漏的修复器如果对象仍然被业务代码错误持有增加一个onMemoryLevel()并不会自动修复引用关系。这个回调适合做的是主动释放本来就允许释放的资源。内存泄漏、异常对象增长、Native 资源未释放等问题仍然应该通过对应的内存分析和生命周期管理手段定位。八、实际项目可以按这个顺序排查如果已经实现onMemoryLevel()但发现策略没有达到预期可以先确认项目是否基于 Stage 模型、AbilityStage 文件是否已经通过module.json5.srcEntry正确注册再检查回调参数是否使用AbilityConstant.MemoryLevel而不是自己维护数字常量然后确认业务真正清理的是可重建缓存而不是只打印了一条日志最后再检查代码是否把枚举写死成三个值。如果项目面向 HarmonyOS 7还应把 API 26 的版本适配纳入检查范围。官方说明 HarmonyOS 7.0 对应 API 26.0.0与此同时MemoryLevel 在更早的 API 24 变更中已经新增了四个枚举成员。也就是说老文章里“MemoryLevel 就是三档”的写法已经不能直接当作 HarmonyOS 7 下的完整 API 描述。这也是这类基础 API 很容易被忽略的一点接口名字没有变化不代表枚举和行为边界永远不变。开发经验总结onMemoryLevel()本身并不复杂真正值得设计的是回调之后的资源分级策略。MODERATE、LOW、CRITICAL可以分别对应逐渐增强的非必要缓存释放力度真正的业务数据则应该和缓存明确分层不能因为内存紧张就一起清掉。在 HarmonyOS 7 / API 26 工程中还需要注意 MemoryLevel 已经不止早期文档里的三个成员。编写分支时使用官方枚举并给未来扩展保留兜底比依赖0 / 1 / 2更稳妥。最后不要把“调用了释放代码”和“已经获得某个具体内存收益”画等号。onMemoryLevel()负责提供系统压力信号实际释放效果仍然需要结合具体资源类型和内存分析工具验证。如果正在整理项目里的内存缓存可以先做一个很简单的检查每个长期驻留内存的对象能不能明确回答“系统内存紧张时它是否可以丢弃以及丢弃后从哪里恢复”能把这个问题回答清楚onMemoryLevel()才真正有地方落地。❤️ 如果本文帮到了你…请点个赞让我知道你还在坚持阅读技术长文请收藏本文因为你以后一定还会用上如果你在学习过程中遇到bug请留言我帮你踩坑
