Skip to content

[camera_android_camerax] Fix leaked thread per capture in takePicture - #12361

Merged
auto-submit[bot] merged 1 commit into
flutter:mainfrom
toyaji:camerax-imagecapture-main-executor
Aug 12, 2026
Merged

[camera_android_camerax] Fix leaked thread per capture in takePicture#12361
auto-submit[bot] merged 1 commit into
flutter:mainfrom
toyaji:camerax-imagecapture-main-executor

Conversation

@toyaji

@toyaji toyaji commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Fixes flutter/flutter#190523

Problem

ImageCaptureProxyApi.takePicture creates a new single-thread executor on
every call and never shuts it down. The thread it owns is only reclaimed
once its FinalizableDelegatedExecutorService wrapper is finalized, at a
time the plugin does not control.

That executor only delivers an already finished result — it does no real
work:

  • Encoding and file I/O run on CameraX's own IO executor.
  • TakePictureRequest.onResult does nothing but hand the result to the
    executor the plugin passed in.

Fix

Dispatch the result on the main executor instead, which is what this
plugin's other CameraX callbacks already use:

  • CameraControlProxyApi
  • Camera2CameraControlProxyApi
  • ImageAnalysisProxyApi
  • ProcessCameraProviderProxyApi
  • PendingRecordingProxyApi

Note

PreviewProxyApi uses the same per-request executor pattern for
SurfaceRequest.provideSurface. That callback releases a Surface, so it
is left for a separate change.

The two existing takePicture tests now stub the main executor on their
mock Context.

Pre-Review Checklist

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses a thread leak in the camera_android_camerax package by dispatching the takePicture result callback on the main executor instead of a new single-thread executor. Unit tests in ImageCaptureTest.java have been added and updated to verify this change. There are no review comments, so I have no feedback to provide.

@gmackall
gmackall requested a review from camsim99 August 4, 2026 19:37
@stuartmorgan-g stuartmorgan-g added the triage-android Should be looked at in Android triage label Aug 4, 2026

@camsim99 camsim99 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for filing a clear issue and the quick fix! This totally makes sense to me.

@camsim99 camsim99 added the CICD Run CI/CD label Aug 10, 2026
@flutter-dashboard

Copy link
Copy Markdown

This pull request is not mergeable in its current state, likely because of a merge conflict. Pre-submit CI jobs were not triggered. Pushing a new commit to this branch that resolves the issue will result in pre-submit jobs being scheduled.

@camsim99
camsim99 requested review from a team, bparrishMines and reidbaker and removed request for a team August 10, 2026 17:25
@gmackall gmackall added the autosubmit Merge PR when tree becomes green via auto submit App label Aug 11, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Aug 11, 2026
@auto-submit

auto-submit Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/packages/12361, because Pull request flutter/packages/12361 is not in a mergeable state.

Fixes flutter/flutter#190523

`ImageCaptureProxyApi.takePicture` creates a new single-thread executor on
every call and never shuts it down. The thread it owns is only reclaimed
once its `FinalizableDelegatedExecutorService` wrapper is finalized, at a
time the plugin does not control.

That executor only delivers an already finished result — it does no real
work:

- Encoding and file I/O run on CameraX's own IO executor.
- `TakePictureRequest.onResult` does nothing but hand the result to the
  executor the plugin passed in.

Dispatch the result on the main executor instead, which is what this
plugin's other CameraX callbacks already use:

- `CameraControlProxyApi`
- `Camera2CameraControlProxyApi`
- `ImageAnalysisProxyApi`
- `ProcessCameraProviderProxyApi`
- `PendingRecordingProxyApi`

`PreviewProxyApi` uses the same per-request executor pattern for
`SurfaceRequest.provideSurface`. That callback releases a `Surface`, so it
is left for a separate change.

The two existing `takePicture` tests now stub the main executor on their
mock `Context`.
@toyaji
toyaji force-pushed the camerax-imagecapture-main-executor branch from 6a2806f to c85072a Compare August 12, 2026 00:10
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Aug 12, 2026
@toyaji

toyaji commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

@camsim99 @gmackall Rebased to resolve the CHANGELOG conflict — version is now 0.7.4+5 since #12145 took +4. No code changes. Ready for the CICD and autosubmit labels again.

@camsim99 camsim99 added autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD labels Aug 12, 2026
@auto-submit
auto-submit Bot merged commit 456f992 into flutter:main Aug 12, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD p: camera platform-android triage-android Should be looked at in Android triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[camera_android_camerax] takePicture creates a single-thread executor per capture that is never shut down

4 participants