HarmonyOS 7 ImageSRAnalyzer:超分结果租约与销毁栅栏 图像超分的界面很容易写成三个动作选图、处理、展示。真正需要反复判断的却是第四个动作——上一张结果什么时候可以释放。用户连续选择两张图时第一个process()可能后返回页面切走时分析器可能仍有任务新 PixelMap 替换旧 PixelMap 时如果释放顺序写反Image 组件可能读到已经无效的资源。HarmonyOS 7 / API 26 的 Core Vision Kit 新增imageSuperResolution命名空间当前接口声明包含ImageSRAnalyzer.create()、process(request)、返回对象中的pixelMap以及destroy()。这套 API 给出了创建、处理和销毁边界但业务仍要回答谁拥有返回的 PixelMap、迟到结果由谁回收、销毁时如何等待在途任务。本文用SRLeaseBench演示一个结果租约层。任务 ID 为SR-1132-509样例输入fixture_city_720p为 1280×720演示夹具把返回结果标记为 2560×1440页面代次为 17当前持有 1 份结果累计释放 3 份拒绝 1 个迟到结果。三阶段流程完成两段显示 67%状态为ANALYZED_WAITING_SWAP。所有数字都是可核对的演示契约不冒充真实超分性能或设备实测。一、把 PixelMap 看成资源不要只当图片变量ArkTS 变量被覆盖不等于底层图像资源已经正确结束生命周期。官方 Image Kit 指导明确要求在 PixelMap 的异步操作完成、对象不再需要后调用release()。超分返回的 PixelMap 同样需要清晰所有权否则连续处理会把大块像素内存留到不可预测的回收时机。最简单的页面通常有三个引用输入 PixelMap、分析结果 PixelMap、Image 组件正在显示的 PixelMap。它们可能不是同一个对象也不应该共享一个“页面销毁时一起 release”的粗糙策略。输入资源由解码阶段拥有process()返回结果先由任务拥有只有通过代次检查并完成 UI 交换后结果所有权才转给显示租约。租约的含义不是锁而是一条交接规则新结果进入页面前先验代次UI 引用更新后再释放旧结果页面离开时先阻止新交接等待在途任务结算最后释放当前结果并销毁分析器。任何未被接纳的返回值都由产生它的任务立即释放。这个顺序还有一个现实原因。用户操作频率和算法完成顺序没有必然关系。第 17 代请求发出后用户可能立刻发起第 18 代若第 17 代最后才返回不能因为它“成功”就覆盖第 18 代页面。成功只说明能力调用完成不说明结果仍属于当前界面。二、创建分析器时先约定唯一销毁路径第一段代码解决“多个按钮各自创建分析器最后没人知道该销毁哪一个”的问题。AnalyzerOwner只允许一个活动实例并把状态限制为IDLE、READY、DESTROYING、DESTROYED。get()在销毁开始后拒绝继续提供实例。import{imageSuperResolution}fromkit.CoreVisionKittypeOwnerStateIDLE|READY|DESTROYING|DESTROYEDclassAnalyzerOwner{privateanalyzer?:imageSuperResolution.ImageSRAnalyzerprivatecreateTask?:PromiseimageSuperResolution.ImageSRAnalyzerprivatestate:OwnerStateIDLEasyncget():PromiseimageSuperResolution.ImageSRAnalyzer{if(this.stateDESTROYING||this.stateDESTROYED){thrownewError(analyzer unavailable:${this.state})}if(this.analyzer)returnthis.analyzerif(!this.createTask){this.createTaskimageSuperResolution.ImageSRAnalyzer.create()}this.analyzerawaitthis.createTaskthis.stateREADYreturnthis.analyzer}asyncdestroy():Promisevoid{if(this.stateDESTROYED||this.stateDESTROYING)returnthis.stateDESTROYINGconstanalyzerthis.analyzer??awaitthis.createTaskif(analyzer)awaitanalyzer.destroy()this.analyzerundefinedthis.createTaskundefinedthis.stateDESTROYED}}这里没有在get()失败后无限重试。创建失败可能来自能力不可用、设备限制或环境问题业务应捕获错误、提供降级路径并记录错误码而不是在按钮回调里递归创建。destroy()也必须幂等页面生命周期、异常收口和主动退出可能同时请求销毁重复调用不应再次操作已释放实例。当前公开变更资料只确认process()接受visionBase.Request没有理由在文章里猜测不存在的请求字段。SRLeaseBench把请求构造放在单独的RequestFactory中实际项目应按当前 API 参考和 SDK 类型声明创建 Request。生命周期层只接收一个已经合法构造的 Request不侵入其内部结构。三、任务拥有返回值页面只接纳当前代次第二段代码解决“迟到成功结果覆盖新页面”的问题。每次选择新图片都会增加generation。任务返回后先核对页面是否仍活跃、代次是否一致不满足时立即释放返回 PixelMap。只有当前代次可以进入pendingSwap。import{image}fromkit.ImageKitimport{visionBase}fromkit.CoreVisionKitinterfacePendingResult{generation:numberpixelMap:image.PixelMap}classSRLeaseController{privateownernewAnalyzerOwner()privategeneration:number16privateactive:booleantrueprivatecurrent?:image.PixelMapprivatependingSwap?:PendingResultprivateinFlightnewSetPromisevoid()releasedCount:number0lateDropped:number0submit(request:visionBase.Request):number{constgenerationthis.generationconsttaskthis.run(generation,request)this.inFlight.add(task)voidtask.finally(()this.inFlight.delete(task))returngeneration}privateasyncrun(generation:number,request:visionBase.Request):Promisevoid{constanalyzerawaitthis.owner.get()constresponseawaitanalyzer.process(request)constresultresponse.pixelMapif(!this.active||generation!this.generation){result.release()this.releasedCount1this.lateDropped1return}this.pendingSwap{generation,pixelMap:result}}}任务集合保存的是结算 Promise用于销毁栅栏finally必须移除自身否则完成任务也会长期滞留。实际代码还要在run()周围捕获BusinessError区分创建失败、处理失败和页面取消。失败路径没有返回 PixelMap 时不能调用 release已经得到结果后发生 UI 交换错误则必须由 pending 租约回收。lateDropped1表示演示中确实有一份结果被代次检查拒绝不代表底层算法可取消。逻辑拒绝只能防止旧结果进入 UI不能减少已经发生的计算。若能力后续提供取消接口再把取消与代次校验组合在当前已核实接口之外不虚构cancel()。项目结构中AnalyzerOwner.ets管理能力实例SRLeaseController.ets管理代次和 PixelMapRequestFactory.ets对接当前 SDK 请求类型SRLeasePage.ets只渲染状态。DevEco Studio 风格配图是根据这份结构生成的技术演示不是实际 IDE 截图。四、UI 交换完成后才能释放旧结果pendingSwap并不等于页面已经安全显示新图。如果收到结果就立刻释放current而 UI 仍在这一帧引用旧 PixelMap可能出现闪烁、空白或资源访问错误。更稳妥的做法是把交换拆成“准备、提交、清理”三步。第三段代码解决“新旧 PixelMap 释放顺序不明确”和“页面退出时仍有任务返回”的问题。commitSwap()先把新对象设为当前再把旧对象放到下一次 UI 调度后释放。示例使用一个抽象的afterNextFrame实际项目应接入已经验证的 UI 帧回调或组件更新时机不能把任意setTimeout(0)当作渲染完成证明。asynccommitSwap(afterNextFrame:(cb:()void)void):Promiseboolean{constpendingthis.pendingSwapif(!pending||pending.generation!this.generation||!this.active){returnfalse}constoldthis.currentthis.currentpending.pixelMapthis.pendingSwapundefinedafterNextFrame((){if(old){old.release()this.releasedCount1}})returntrue}asyncclose():Promisevoid{if(!this.active)returnthis.activefalsethis.generation1awaitPromise.allSettled([...this.inFlight])if(this.pendingSwap){this.pendingSwap.pixelMap.release()this.pendingSwapundefinedthis.releasedCount1}if(this.current){this.current.release()this.currentundefinedthis.releasedCount1}awaitthis.owner.destroy()}关闭顺序不能反过来。若先销毁分析器再等待在途process()底层行为可能进入未定义区若先释放页面结果但没有把active置为 false下一份返回值又会进入 pending。正确顺序是关闭接纳入口、使旧代次失效、等待任务结算、回收两类 PixelMap、最后销毁分析器。等待全部任务也需要超时策略。示例为了突出所有权没有展开超时生产代码应记录等待预算超时后进入可诊断的CLOSE_TIMEOUT并依据官方能力保证决定是否继续销毁。不能在不知道底层状态时简单忽略 Promise也不能伪造“资源已释放”日志。五、67% 表示交换尚未提交运行页将流程拆为三个阶段DECODED、ANALYZED、SWAPPED。演示已经完成前两步因此显示 67%不是算法推理进度。当前状态为ANALYZED_WAITING_SWAP说明结果已进入 pending 租约但 UI 交换仍等待下一帧提交。页面展示任务SR-1132-509、夹具fixture_city_720p、输入 1280×720、演示输出 2560×1440、generation 17、活动租约 1。顶部状态栏固定为 11:32、Wi‑Fi、5G、信号和 74% 电量。红色标注只圈出 67% 与ANALYZED_WAITING_SWAP提醒读者不要把“分析完成”写成“界面已完成”。输入与输出尺寸来自测试夹具不是本文对超分倍率作出的通用承诺。真实返回结果应通过 PixelMap 图像信息读取并将实际尺寸写入日志。不同输入、设备或能力版本的输出策略应以当前 API 文档和实际结果为准。六、诊断页要能对上每一次 release详情页按租约列出资源输入 PixelMap 由解码器持有pending 结果属于 generation 17当前显示租约尚未替换历史累计释放 3迟到结果拒绝并释放 1。销毁栅栏状态为OPEN在途任务为 0因此允许提交 UI 交换。03 与 04 的职责不同。总览页回答“流程到了哪一步”诊断页回答“哪一方拥有哪份 PixelMap、为何还不能 destroy”。时间、任务 ID、generation 和 74% 电量保持一致日志使用createPASS、processPASS、pending1、released3、lateDropped1、barrierOPEN。每次 release 日志应带资源 ID 与原因如replaced、late_generation、page_close、pending_abort。只打印总数无法定位重复释放。资源 ID可以由租约层生成不要依赖 PixelMap 内部地址。释放后立即从对应字段移除引用避免后续分支误判对象仍可用。七、异常路径比成功路径更值得先画出来创建分析器失败时页面保持原图并显示能力不可用process()失败时当前显示结果不能被清空新结果成功但页面已经关闭时任务负责立即释放UI 交换失败时pending 结果仍归租约层不能转成 current销毁失败时记录状态和错误不应把 owner 标成完全结束。输入 PixelMap 的释放也要和请求生命周期对齐。若visionBase.Request在process()完成前仍引用输入资源解码器不能在提交请求后立即 release。请求字段与复制语义必须以当前 SDK 文档为准不确定时至少把输入租约保持到 Promise 结算再释放或交还上层。页面后台化是否销毁分析器取决于产品策略。短时间切后台可能保留实例以降低重建成本长期后台或内存压力下则应释放。无论选择哪种策略都需要唯一 owner 和同一套栅栏而不是在onPageHide、aboutToDisappear、错误弹窗里分别调用 destroy。八、与已有并发优化不是同一个问题队列限流解决“同时跑多少任务”电量门禁解决“何时允许启动”回归检测解决“结果质量是否退化”。本文处理的是另一个维度每一份成功返回的 PixelMap 由谁接收、何时进入 UI、何时释放、分析器何时可以销毁。即使并发度固定为 1这个问题仍然存在。同样代次校验不是取消。它只是保证旧结果不会污染新页面。资源租约也不是内存优化的代名词它首先建立可证明的所有权然后才有资格谈峰值内存。没有所有权图任何“及时 release”都可能过早或重复。九、结论与边界SRLeaseBench最终把三个容易混淆的完成时刻分开算法 Promise 完成、结果被当前页面接纳、UI 交换后旧资源释放。演示状态停在ANALYZED_WAITING_SWAP67% 正好说明第三步尚未提交而不是用一个 100% 掩盖资源还在 pending。当前一手资料确认了 API 26 中ImageSRAnalyzer的创建、处理、结果 PixelMap 与销毁接口也确认 PixelMap 不再使用后应手动 release。本文没有补造倍率、取消、进度回调或隐藏参数。请求构造、设备支持和错误码仍应以项目使用的最新 SDK API 参考为准。可以复用的工程结论只有四条分析器由唯一 owner 管理每次请求携带页面代次未被接纳的结果由任务立即释放关闭时先封入口、等结算、收 PixelMap、再 destroy。把这四条写进封装层超分页面才不会在连续选图和页面退出时留下无法解释的资源状态。十、参考资料华为开发者联盟Core Vision Kit API 26 变更清单ImageSRAnalyzer.create/process/destroy与ISPResponse.pixelMaphttps://developer.huawei.com/consumer/en/doc/harmonyos-releases/js-apidiff-corevisionkit-7001华为开发者联盟Image Kit 图片解码与 PixelMap / ImageSource 释放指导https://developer.huawei.com/consumer/en/doc/harmonyos-guides-V14/image-decoding-V14华为开发者联盟图像超分能力介绍https://developer.huawei.com/consumer/cn/hiai/engine/image-super-resolution