HarmonyOS 7 Core Vision Kit + PixelMap:ImageSRAnalyzer 串行任务队列、结果缓存与 destroy 生命周期收口【鸿蒙心迹】
图像超分 Demo 最容易写成“选一张图 → process → 显示结果”。真正开始做产品以后,问题会马上变成另一套:用户连续点三次处理怎么办?同一张图重复点要不要重新推理?页面退出时 Analyzer 还在不在?结果 PixelMap 谁持有?这次我就围绕这些工程问题做一个
PhotoSRQueue。

当前 HarmonyOS 7 / API 26 的 Core Vision Kit 已经提供 imageSuperResolution。核心链路很清楚:
ImageSRAnalyzer.create() 创建分析器,process(request) 接收一张图片并返回 ISPResponse,结果通过 pixelMap 获取;不用以后调用 destroy() 释放服务资源。
真正需要业务自己补的,是调度层。
这次运行数据固定为:
- Task ID:
sr_20261001_03 - State:
COMPLETED - Source:
photo_1280x720.jpg - Result PixelMap:
READY - First run:824 ms
- Repeat:
CACHE_HIT 18 ms - Queue:0
- Cache entries:1
- Analyzer:
ACTIVE
一、第一次能跑通以后,我先把按钮连点三次
最初的版本只有十几行:
const analyzer =
await imageSuperResolution.ImageSRAnalyzer.create();
const response =
await analyzer.process(request);
this.resultPixelMap = response.pixelMap;
单次点按钮完全正常。
但当我连续选择多张图、快速点击处理时,页面开始出现两个明显问题:
第一,多个 Promise 同时更新 resultPixelMap,最后显示出来的不一定是最后一次点击对应的结果。
第二,页面状态没有任务概念,只剩一个 RUNNING。前一个任务还没结束,后一个又进来了,UI 根本解释不清现在处理的是谁。
官方接口本身描述的是“单张图片请求”,所以我没有把批量任务能力强行归到系统 API 上,而是在业务层包一层串行队列。
二、Analyzer 只创建一次,不跟着每个任务反复创建
这段代码解决的问题,是页面生命周期内复用同一个分析器。
import {
imageSuperResolution
} from '@kit.CoreVisionKit';
private analyzer:
imageSuperResolution.ImageSRAnalyzer | null = null;
async aboutToAppear(): Promise<void> {
try {
this.analyzer =
await imageSuperResolution.ImageSRAnalyzer.create();
this.analyzerState = 'ACTIVE';
} catch (error) {
this.analyzerState = 'ERROR';
console.error(
`create analyzer failed: ${JSON.stringify(error)}`
);
}
}
这里我没有把 create() 放进每一次“开始处理”按钮。
原因很直接:Analyzer 是一个服务对象,不是一个一次性临时变量。
页面还在使用这项能力时,重复创建没有必要;页面离开后再统一销毁,生命周期更清楚。
这也让任务队列只关心“什么时候执行 process”,不需要每个任务都重新处理初始化逻辑。
三、我给 process 外面包了一层串行任务队列
当前代码解决的是:同一时间只让一个 SRTask 进入实际 process()。
interface SRTask {
id: string;
key: string;
pixelMap: PixelMap;
state: 'WAITING' | 'RUNNING' |
'COMPLETED' | 'FAILED';
}
private queue: SRTask[] = [];
private running: boolean = false;
async enqueue(task: SRTask): Promise<void> {
this.queue.push(task);
if (this.running) {
return;
}
await this.processNext();
}
private async processNext(): Promise<void> {
if (this.queue.length === 0) {
this.running = false;
return;
}
this.running = true;
const task = this.queue.shift()!;
try {
task.state = 'RUNNING';
await this.executeTask(task);
task.state = 'COMPLETED';
} catch (error) {
task.state = 'FAILED';
} finally {
await this.processNext();
}
}
它没有试图证明“Core Vision Kit 不能并发”。
我只是基于当前产品需求做了一个更稳定的调度选择:一次处理一张,UI 状态按任务顺序收口。
这种写法还有一个好处。
后面如果要加取消、失败重试、任务优先级,不需要去改 ImageSRAnalyzer 调用层,只改队列就行。
四、真正调用官方 API 的部分反而很短
这段代码解决的是把输入 PixelMap 封装成 visionBase.Request,再拿回结果。
import {
imageSuperResolution,
visionBase
} from '@kit.CoreVisionKit';
private async runSR(
input: PixelMap
): Promise<PixelMap> {
if (!this.analyzer) {
throw new Error('Analyzer not ready');
}
const imageData: visionBase.ImageData = {
pixelMap: input
};
const request: visionBase.Request = {
inputData: imageData
};
const response:
imageSuperResolution.ISPResponse =
await this.analyzer.process(request);
return response.pixelMap;
}
这才是系统能力真正负责的部分。
官方 API 26 文档当前给出的接口也是这条链路:ImageSRAnalyzer.create()、process(request)、ISPResponse.pixelMap 和 destroy()。
另外,图像超分一次请求只处理一张图片,所以本文的“队列”完全属于应用侧调度,不是把多张图片一起塞进一次 process。
五、第二次点同一张图,我不想再跑一遍推理
PhotoSRQueue 的第二个改动是缓存。
当前 Demo 给输入图生成一个业务 key。正式项目可以把文件 URI、大小、修改时间或者内容摘要组合起来,本文为了讲清逻辑,只假设已经得到稳定 key。
这段代码解决的是相同输入重复处理:
private cache:
Map<string, PixelMap> = new Map();
private async executeTask(
task: SRTask
): Promise<void> {
const cached =
this.cache.get(task.key);
if (cached) {
this.resultPixelMap = cached;
this.lastResult = 'CACHE_HIT';
return;
}
const result =
await this.runSR(task.pixelMap);
this.cache.set(task.key, result);
this.resultPixelMap = result;
this.lastResult = 'PROCESSED';
}
这就是截图里第一次 824ms、第二次 18ms 的来源。
18ms 不是 ImageSRAnalyzer 第二次突然变快,而是第二次根本没有进入超分推理,只完成缓存查找和 UI 更新。
如果文章不把这一点说清楚,很容易把业务缓存效果误写成系统性能提升。
缓存同样有边界。
PixelMap 占内存,正式项目不能让 Map 一直增长。至少要做容量限制、最近使用淘汰和主动释放策略。
当前 Demo 只保留一个结果,所以截图里 Cache entries = 1。
六、图片资源不是只有 Analyzer 需要释放
做图像类能力时,最容易把注意力全放到 AI 服务上。
但输入图通常还经历:
- Photo Picker 返回 URI;
- fileIo 打开文件;
- ImageSource 解码;
- createPixelMap;
- Analyzer process;
- 输出 PixelMap;
- UI 展示。
如果这些对象全部长期持有,页面即使只处理三张图,内存也可能迅速增大。
我现在的做法是:
输入 PixelMap 在任务结束、确认后续不再使用时释放;临时 ImageSource 和 file 及时关闭;输出结果如果进入缓存,就由缓存层统一管理,而不是页面和缓存各留一份引用。
官方示例本身也会在图像处理完成后释放输入 PixelMap、ImageSource、文件句柄,最后销毁 Analyzer。
七、destroy 一定要和页面退出形成闭环
这段代码解决的,是用户离开页面以后不再保留超分服务。
async aboutToDisappear(): Promise<void> {
this.queue = [];
this.running = false;
if (this.analyzer) {
try {
await this.analyzer.destroy();
} finally {
this.analyzer = null;
this.analyzerState = 'DESTROYED';
}
}
}
这里有一个工程细节。
清空队列不代表已经在执行的 Promise 被“强制取消”。
如果业务要求处理中立即退出,还要在任务结果回来时判断页面是否已经 disposed,避免继续写 UI。
所以正式版本我会再加一个 session token:
页面进入时生成 token,任务完成时比对 token;页面离开以后旧 token 失效。
这样即使底层处理已经发出,也不会让过期结果覆盖新页面。
八、我更愿意把 Analyzer 看成页面级资源,而不是按钮函数
做到这里以后,工程结构也从单页变成:
entry/src/main/ets/
├── model/
│ └── SRTaskState.ets
├── pages/
│ └── SRLabPage.ets
└── service/
├── SRCache.ets
└── SRTaskQueue.ets

DevEco 图里,中间区域主要是 SRTaskQueue.ets。
右侧运行页显示:
Task: sr_20261001_03
State: COMPLETED
First run: 824 ms
Repeat: CACHE_HIT 18 ms
Queue: 0
Cache entries: 1
Analyzer: ACTIVE
底部日志同样对应这组数据。
这张图我最想表达的不是“代码很多”,而是 Analyzer 已经从 UI 页面里被剥离出来。
页面负责选图和显示状态;队列负责顺序;缓存负责复用;Core Vision Kit 只负责真正的图像超分。
九、为什么最终截图里 Analyzer 还是 ACTIVE
运行截图里任务已经 COMPLETED,但 Analyzer 仍然显示 ACTIVE。

这是故意保留的状态。
因为用户还停留在 Photo SR Queue 页面里,下一秒还可能再选一张图。这个时候销毁 Analyzer,再点一次又 create,一来一回没有必要。
真正的释放时机,是页面离开或者业务明确结束超分功能以后。
所以当前状态组合是:
Task = COMPLETED
Queue = 0
Analyzer = ACTIVE
这三个状态没有冲突。
任务完成描述的是业务任务,Queue=0 描述的是没有等待项,Analyzer=ACTIVE 描述的是服务仍可复用。
把这三层混成一个“完成/未完成”,后面一定会越来越难维护。
十、这次最重要的不是 824ms,而是边界被拆清楚了
图像超分的系统 API 其实很短。
真正让一个 Demo 变成可继续扩展的工程,需要把几个边界拆开:
Analyzer 生命周期:页面级资源,进入创建,退出 destroy。
Task 生命周期:每一张图自己的 WAITING / RUNNING / COMPLETED / FAILED。
Cache 生命周期:结果复用,但必须有容量和释放策略。
UI 生命周期:只展示当前仍然有效的任务结果。
当前截图里的 824ms、18ms 只是这一版 Demo 的一次运行数据,不应该当成设备性能结论。
真正值得留下的,是这条调用闭环:
选图 → PixelMap → 入队 → 查缓存 → process → 缓存结果 → UI 展示 → 页面退出 destroy。
后面无论要加批量导入、任务取消、磁盘缓存还是后台处理,都可以继续沿着这条结构扩展,而不是把所有逻辑重新塞回一个按钮回调里。
参考资料
- HarmonyOS 7 Core Vision Kit API 变更
https://developer.huawei.com/consumer/en/doc/harmonyos-releases/js-apidiff-corevisionkit-7001 - 图像超分开发者实践合集
https://developer.huawei.com/consumer/cn/forum/topic/0204224159448225375 - PixelMap 图像变换
https://developer.huawei.com/consumer/cn/doc/HarmonyOS-Guides/image-transformation
更多推荐



所有评论(0)