Direct answer
Treat every CameraX ImageProxy as a short-lived resource with one explicit owner and one close point. Android’s CameraX image-analysis guide says the analyzer is responsible for calling ImageProxy.close() after analysis and warns not to close the wrapped Media.Image directly. If a local AI pipeline copies pixels or converts a frame before asynchronous inference, close the proxy once the application no longer needs its planes; the downstream task should own only the copied, bounded data. Do not keep a camera-backed frame open for the full duration of model inference unless that duration and backpressure policy are intentionally designed and tested. A resource leak can present as stalled analysis, rising memory, old frames being processed, or camera recovery failure. These symptoms do not automatically prove a leak: the analyzer may be slow, the queue may be configured to block, inference may retain copied bitmaps, or cleanup may be missing only on exception paths. Instrument ownership transitions—received, copied, submitted, completed, cancelled, failed, closed—and attach a unique frame ID. Verify that each accepted frame reaches exactly one terminal close, while rejected or dropped frames are also released. Avoid a boolean “busy” flag that loses which frame it represents when callbacks overlap. Keep image acquisition, preprocessing, and inference ownership separate. The analyzer receives a proxy from CameraX. A preprocessing component may produce an app-owned bitmap or tensor; an inference task may hold that copy until completion. Make the copy policy explicit because duplicating full-resolution frames can also increase memory use. The project’s MEMORY-01 fact cautions that input tensors, runtime buffers, and allocator behavior contribute to memory beyond model file size. This guide therefore does not prescribe a universal buffer count or RAM threshold. Bound queue depth and choose a documented backpressure strategy; if the model cannot keep pace, dropping stale frames may better serve a live camera UI than accumulating a backlog, but the product must define which frames are retained. Lifecycle and error cases matter as much as the happy path. Bind the use case to a lifecycle owner, clear or unbind the analyzer when capture ends, and make cancellation release both proxy and app-owned copies according to their owners. In a finally path, close the proxy even if color conversion, OCR parsing, model initialization, or UI collection fails. Do not close the same proxy from both the analyzer and a coroutine completion callback. If inference continues after a screen disappears, keep the copied input under a longer-lived task owner and ensure the user’s stop action has a defined cleanup boundary. Test with a small repeated-capture script and inject slow inference, exceptions, cancellation, navigation, camera unbind, and process recreation. Track outstanding proxy count, app-owned copy count, frame age, dropped-frame events, and successful camera restart; these are test observations, not universal performance metrics. Verify ownership using code review or a wrapper that enforces close-once semantics. Keep private photos out of the test corpus, and do not turn a memory trace from one phone into broad device support or performance claims. The useful result is a reproducible ownership timeline showing where each buffer is released and whether the camera can resume after failure.
Camera preview stops after several local OCR requests
An Android screen streams CameraX frames into preprocessing and a local model. It works for a short demo, then the preview stalls after slow inference or navigation. No one knows whether the proxy remained open, copied pixels accumulated, or analyzer work was queued.
Decision
Assign one owner and terminal close point to each ImageProxy, bound app-owned copies and queued work, and test all exceptional exits with frame IDs.
Expected outcome
The team can distinguish leaked camera buffers from slow inference or retained copies and can verify a safe stop/rebind path without publishing a device-wide memory claim.
Evidence to collect
The claim becomes reviewable only when the following evidence is attached to the same artifact and test run:
- Frame ownership map
- Repeated capture and failure fixtures
- Memory and camera recovery trace
A device badge must come from an exact device, OS, artifact, runtime, backend, and workload record, not parameter-count intuition.
Implementation workflow
- Draw the ownership timeline. For each frame, identify CameraX proxy owner, any pixel/bitmap/tensor copy owner, inference task owner, and UI result owner. Define the exact transition at which CameraX planes are no longer needed and who calls ImageProxy.close().
- Copy only what must outlive the analyzer. If asynchronous inference needs pixels after analyze returns, create a bounded app-owned representation and record its dimensions and lifecycle. Release the proxy immediately after the copy succeeds; if copying fails, close in a finally path. Avoid keeping multiple full-resolution frames without a measured reason.
- Select queue and backpressure behavior. Configure and document the ImageAnalysis backpressure strategy and queue depth. Add a deliberate policy for slow inference: process latest, drop, or serialize frames. Ensure every dropped/rejected proxy is closed and every submitted task has a cancellation owner.
- Inject failure and lifecycle exits. Test success, preprocessing exception, model-load failure, inference cancellation, rapid navigation, analyzer replacement, unbind, and screen recreation. Record frame ID, close count, copied-buffer release, and outstanding work after each exit.
- Verify memory and camera recovery. Repeat a fixed synthetic or permission-cleared image sequence, inspect bounded outstanding resources rather than infer a universal RAM limit, then stop and bind again. Report observed counts and exact device/build only; keep personal images and raw OCR text out of diagnostic output.
Keep each transition observable. A failure should identify the stage, artifact, runtime, and recovery action without logging private user content.
Acceptance criteria
- Every received, rejected, or dropped ImageProxy has exactly one documented close path, including exceptions and cancellation.
- Any data that outlives analyze() is an explicitly app-owned copy with bounded queue/retention policy.
- Tests cover analyzer failure, slow inference, navigation/unbind, cancellation, and camera restart without accumulating outstanding frames.
- Memory or performance findings are scoped to the test configuration and do not claim universal device support or a fixed RAM threshold.
Decision table
| Dimension | Choice or evidence | How to interpret it |
|---|---|---|
| Hold ImageProxy through inference | Avoid unless ownership and latency are intentionally bounded | Keeps camera-backed resources alive and can starve the producer when inference is slow. |
| Copy then close proxy | Use a bounded app-owned buffer when work must be asynchronous | Separates CameraX lifetime from inference; copies still consume memory and need cleanup. |
| Queue every frame | Only if product semantics need every frame and capacity is tested | Can build stale backlog; a live preview may prefer dropping outdated frames with explicit policy. |
Failure patterns to prevent
- Retaining every frame for inference
- Closing frames after UI disposal only
Also prevent silent fallback, unpinned artifacts, missing cancellation, and conclusions that combine unlike configurations. Store unsuccessful runs alongside successful ones.
Minimum reproducibility record
| Layer | Record |
|---|---|
| Device | Manufacturer, model, chipset, RAM class, operating-system build |
| Software | Application version and git commit |
| Model | Family, variant, revision, format, file length, hash, quantization |
| Runtime | Name, revision, requested backend, observed backend evidence |
| Workload | Fixture revision, input hash, prompt hash, output policy |
| Outcome | Completed, failed, cancelled, fallback, and privacy-safe diagnostics |
Release checklist
- ☐ The primary query is answered without an unsupported number.
- ☐ Every artifact and runtime is pinned.
- ☐ The representative task and failure policy are explicit.
- ☐ Lifecycle, cancellation, cleanup, and fallback are tested.
- ☐ User-content and network boundaries are documented.
- ☐ Result wording applies only to the recorded configuration.
- ☐ The page links to raw method or evidence when results are added.
Sources and related evidence
Chinese deployment and troubleshooting content is organized in the .奇连 AI 端侧专题.
Frequently asked questions
Who should close an ImageProxy?
The analyzer/application code that accepted ownership should close it with ImageProxy.close() once analysis no longer needs the camera buffer. Do not close the wrapped Media.Image directly, and do not give two callbacks competing close ownership.
Can I pass ImageProxy to a background inference coroutine?
Only if the ownership and lifetime are explicit and the proxy remains valid until the task ends. In most asynchronous pipelines, copy the needed data into a bounded app-owned buffer, then close the proxy before the long-running model work.
Does a stalled preview prove ImageProxy leakage?
No. Slow analysis, backpressure configuration, inference backlog, retained copies, or lifecycle mistakes can look similar. Track frame IDs and release events, then inject controlled delay and failure to identify the missing transition.