内存与设备证据

CameraX ImageProxy 生命周期与本地 AI 图像帧泄漏测试

明确相机帧、应用复制缓冲和推理任务各自的所有者与释放时点。

审核状态: 内容已校验;中文稿供用户审核 · 2026-10-04 · Fact IDs: MEMORY-01, COVE-OCR-01

直接回答

应把每个 CameraX ImageProxy 当作短生命周期资源,为它指定一个明确所有者和唯一关闭点。Android CameraX 图像分析指南要求 analyzer 完成分析后调用 ImageProxy.close(),并提醒不要直接关闭其包装的 Media.Image。如果本地 AI 管线会在异步推理前复制像素或转换帧,应在应用不再需要 CameraX 图像平面时关闭代理;后续任务只持有有界的应用自有数据。除非推理时长和背压策略经过有意设计与测试,否则不要让相机帧一直存活到模型推理结束。 资源泄漏可能表现为图像分析停滞、内存持续上升、处理过期帧,或相机难以恢复。但这些症状不一定都证明 ImageProxy 泄漏:analyzer 也可能处理太慢,队列策略可能阻塞,推理可能长期持有复制后的位图,或只有异常路径漏了清理。应记录所有权转换:收到、复制、提交、完成、取消、失败、关闭,并给每帧唯一标识。确认每个已接收帧最终只关闭一次;被拒绝或丢弃的帧也要释放。不要用一个忙碌布尔值代表所有帧,否则回调并发时无法知道它指向谁。 应分开管理图像采集、预处理和推理所有权。analyzer 接收 CameraX 代理;预处理组件可能生成应用自有位图或张量;推理任务则在完成前持有该复制数据。复制策略必须写明,因为复制全分辨率帧也会增加内存。项目 MEMORY-01 事实指出,输入张量、运行时缓冲和分配器行为都会影响内存,因此本文不给出通用缓冲数量或 RAM 阈值。队列应有界,背压策略应有文档。模型跟不上帧率时,实时相机界面可能更适合丢弃旧帧,而不是积累延迟;但产品必须明确哪些帧需要保留。 生命周期和错误路径与成功路径同样重要。将用例绑定到生命周期所有者,结束采集时清除 analyzer 或解绑 use case;取消任务时,应根据所有权分别释放代理与应用自有复制数据。即使颜色转换、OCR 解析、模型初始化或界面状态收集抛错,也要在 finally 路径关闭代理。不能让 analyzer 与协程完成回调同时争抢关闭同一代理。如果推理任务在页面消失后继续运行,就让较长生命周期的任务所有者持有复制输入,并为用户停止操作定义清晰清理边界。 测试可使用短时间重复采集脚本,并注入慢推理、异常、取消、页面导航、相机解绑和进程重建。跟踪未结束代理数、应用复制缓冲数、帧龄、丢帧事件和相机重新启动结果;这些是测试观察值,不是普遍性能指标。可通过代码审查或强制 close-once 的包装器核对所有权。测试语料不要包含私人照片,也不能把一台手机上的内存轨迹外推为全面设备兼容或性能结论。有效产出应是一条可复现的所有权时间线,显示每个缓冲何时释放,以及失败后相机是否能恢复。 图像数据跨越线程时还要处理超时与结果过期。一个任务即使晚些时候完成,也可能属于已经关闭的页面或用户不再查看的旧帧。任务标识和帧序号应随复制缓冲一起传递;结果回到 UI 前要确认其仍对应当前会话。过期输出应丢弃或另行标记,而不是覆盖新帧的结果。这样既减少不必要的积压,也避免把生命周期竞态误诊为 OCR 模型随机改变。 重启测试应覆盖相机 use case 解绑、重新绑定、权限暂时不可用、推理取消以及组件异常后的恢复。每一步都要确认相机资源释放和应用自有图像数据被清理;不要通过重启整个 Activity 来掩盖重复持有。公开报告保留帧 ID、阶段、资源计数和错误类别即可,不能附原始照片或完整识别文字。对用户而言,失败应有可理解提示与重试/退出操作;对工程团队而言,追踪字段要足以找到哪一个所有者漏了关闭。

连续本地 OCR 后相机预览停止

Android 屏幕将 CameraX 帧送入预处理和本地模型。短演示正常,但慢推理或页面导航后预览停滞。团队不确定是代理未关闭、复制像素堆积,还是 analyzer 队列积压。

决策

为每个 ImageProxy 指定单一所有者和终止关闭点,限制应用自有复制缓冲及队列,并用帧 ID 测试所有异常出口。

预期结果

团队能区分相机缓冲泄漏、推理变慢和复制数据保留,并验证安全停止/重新绑定流程,而不发布全机型内存主张。

需要收集的证据

只有将以下证据与同一模型文件和测试运行关联起来,结论才可复核:

  • MEMORY-01:推理内存还受输入张量、运行时缓冲与分配器影响,不能由模型文件大小推断。
  • COVE-OCR-01:Cove Photo 将可见文本结构化存储,并提供复制与 OCR 历史导出。

设备兼容结论须绑定具体设备、系统、模型、运行时、后端和工作负载,不能靠参数量推测。

实施流程

  1. 画出所有权时间线. 对每帧列明 CameraX 代理所有者、像素/位图/张量复制所有者、推理任务所有者和 UI 结果所有者。定义 CameraX 图像平面何时不再需要,以及由谁调用 ImageProxy.close()。
  2. 只复制必须跨出 analyzer 生命周期的数据. 异步推理需要 analyzer 返回后继续使用像素时,创建有界的应用自有表示,并记录尺寸和生命周期。复制成功后立即关闭代理;复制失败也要在 finally 中关闭。没有测量理由时,不要持有多张全分辨率帧。
  3. 选择队列和背压行为. 配置并记录 ImageAnalysis 背压策略和队列深度。为慢推理定义明确策略:处理最新帧、丢弃旧帧或串行化;每个被丢弃/拒绝的代理都要关闭,每个已提交任务都要有取消所有者。
  4. 注入失败与生命周期出口. 测试成功、预处理异常、模型加载失败、推理取消、快速导航、替换 analyzer、解绑和页面重建。记录帧 ID、关闭次数、复制缓冲释放情况和每个出口后的未完成工作。
  5. 验证内存边界与相机恢复. 重复固定的合成或已获准图像序列,检查未释放资源是否有界,而非推导通用 RAM 限额;停止后重新绑定相机。报告只对应被测设备和构建,诊断不保存个人照片或原始 OCR 文本。

让每个阶段的状态都可观察。失败信息应指出阶段、模型、运行时和恢复动作,同时避免记录用户私密内容。

验收标准

  • 每个已接收、拒绝或丢弃的 ImageProxy 都有且只有一个关闭路径,涵盖异常和取消。
  • analyze() 结束后仍需使用的数据是明确归应用所有的复制缓冲,并有队列和保留上限。
  • 测试覆盖 analyzer 失败、慢推理、导航/解绑、取消和相机重启,不会积累未完成帧。
  • 内存或性能结果限定到测试配置,不声称普遍机型支持或固定 RAM 阈值。

决策对照表

维度选择或证据解读方式
推理期间持有 ImageProxy除非所有权与时延严格有界,否则避免延长相机资源生命;推理变慢时可能让生产者缺少可用帧。
复制后关闭代理异步处理确实需要像素时使用有界应用缓冲分离 CameraX 与推理生命周期;复制仍占内存且必须清理。
排队保留每一帧只有产品需要且容量经过测试时才采用容易积累过期帧;实时预览可按明确策略丢弃旧帧。

需要避免的失败模式

  • 推理尚未完成时长期持有相机 ImageProxy,阻塞生产者。
  • 只在成功路径关闭代理,异常、取消和页面退出后留下未释放帧。

还要避免静默回退、版本未固定、缺少取消处理,以及把不同配置的结果混在一起下结论。失败运行也应与成功记录一并保存。

最小复现记录

层级记录内容
设备厂商、型号、芯片、内存档位、操作系统版本
软件应用版本和 Git 提交号
模型系列、变体、版本、格式、文件长度、摘要、量化方式
运行时名称、版本、请求后端及实际后端证据
工作负载样例版本、输入摘要、提示词摘要、输出策略
结果完成、失败、取消、回退及保护隐私的诊断信息

发布检查表

  • ☐ 主要搜索问题已回答,且没有未经支持的数字。
  • ☐ 所有模型文件和运行时版本均已固定。
  • ☐ 代表性任务和失败处理策略均有说明。
  • ☐ 生命周期、取消、清理和回退均已测试。
  • ☐ 用户内容与网络边界均有说明。
  • ☐ 结论只适用于被记录的配置。
  • ☐ 新增测试结果时链接到原始方法或证据。

来源与相关证据

中文部署与排错专题见。奇连 AI 端侧专题。

常见问题

ImageProxy 应由谁关闭?

接收所有权的 analyzer/应用代码应在不再需要相机缓冲时调用 ImageProxy.close()。不要直接关闭包装的 Media.Image,也不要让两个回调争抢关闭责任;每帧只关闭一次。

可以把 ImageProxy 传给后台推理协程吗?

只有在所有权和生命周期明确、并确保任务期间代理持续有效时才可以。多数异步管线更适合把所需像素复制到有界应用缓冲,再在长时间推理前关闭代理。

预览停滞是否证明 ImageProxy 泄漏?

不能。慢分析、背压设置、推理积压、复制数据保留和生命周期错误都可能类似。为帧记录身份与释放事件,再注入延时和失败以找出缺失的转换。