mirror of
https://github.com/flutter/packages.git
synced 2025-06-29 14:18:54 +08:00
[camera] Dispose resources correctly on setDescription (#4003)
setDescription for CameraController was not correctly handling resources. It would recreate the _deviceOrientationSubscription each time it was called, which caused the subscription to not be disposed correctly. It also was not disposing of the previous device camera *List which issues are fixed by this PR. You must list at least one issue.* Issue seen here: https://github.com/flutter/flutter/issues/126823
This commit is contained in:
@ -1,3 +1,9 @@
|
||||
## 0.10.5+5
|
||||
|
||||
* Fixes bug where old camera resources were not disposed when switching between camera descriptions.
|
||||
* Fixes bug where _deviceOrientationSubscription was recreated every time the camera description was
|
||||
changed.
|
||||
|
||||
## 0.10.5+4
|
||||
|
||||
* Adds pub topics to package metadata.
|
||||
|
@ -265,7 +265,10 @@ class CameraController extends ValueNotifier<CameraValue> {
|
||||
|
||||
bool _isDisposed = false;
|
||||
StreamSubscription<CameraImageData>? _imageStreamSubscription;
|
||||
FutureOr<bool>? _initCalled;
|
||||
// A Future awaiting an attempt to initialize (e.g. after `initialize` was
|
||||
// just called). If the controller has not been initialized at least once,
|
||||
// this value is null.
|
||||
Future<void>? _initializeFuture;
|
||||
StreamSubscription<DeviceOrientationChangedEvent>?
|
||||
_deviceOrientationSubscription;
|
||||
|
||||
@ -294,11 +297,15 @@ class CameraController extends ValueNotifier<CameraValue> {
|
||||
'initialize was called on a disposed CameraController',
|
||||
);
|
||||
}
|
||||
|
||||
final Completer<void> initializeCompleter = Completer<void>();
|
||||
_initializeFuture = initializeCompleter.future;
|
||||
|
||||
try {
|
||||
final Completer<CameraInitializedEvent> initializeCompleter =
|
||||
Completer<CameraInitializedEvent>();
|
||||
|
||||
_deviceOrientationSubscription = CameraPlatform.instance
|
||||
_deviceOrientationSubscription ??= CameraPlatform.instance
|
||||
.onDeviceOrientationChanged()
|
||||
.listen((DeviceOrientationChangedEvent event) {
|
||||
value = value.copyWith(
|
||||
@ -343,9 +350,9 @@ class CameraController extends ValueNotifier<CameraValue> {
|
||||
);
|
||||
} on PlatformException catch (e) {
|
||||
throw CameraException(e.code, e.message);
|
||||
} finally {
|
||||
initializeCompleter.complete();
|
||||
}
|
||||
|
||||
_initCalled = true;
|
||||
}
|
||||
|
||||
/// Prepare the capture session for video recording.
|
||||
@ -402,6 +409,11 @@ class CameraController extends ValueNotifier<CameraValue> {
|
||||
await CameraPlatform.instance.setDescriptionWhileRecording(description);
|
||||
value = value.copyWith(description: description);
|
||||
} else {
|
||||
if (_initializeFuture != null) {
|
||||
await _initializeFuture;
|
||||
await CameraPlatform.instance.dispose(_cameraId);
|
||||
}
|
||||
|
||||
await _initializeWithDescription(description);
|
||||
}
|
||||
}
|
||||
@ -841,8 +853,8 @@ class CameraController extends ValueNotifier<CameraValue> {
|
||||
_unawaited(_deviceOrientationSubscription?.cancel());
|
||||
_isDisposed = true;
|
||||
super.dispose();
|
||||
if (_initCalled != null) {
|
||||
await _initCalled;
|
||||
if (_initializeFuture != null) {
|
||||
await _initializeFuture;
|
||||
await CameraPlatform.instance.dispose(_cameraId);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing
|
||||
Dart.
|
||||
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera
|
||||
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
|
||||
version: 0.10.5+4
|
||||
version: 0.10.5+5
|
||||
|
||||
environment:
|
||||
sdk: ">=2.19.0 <4.0.0"
|
||||
|
@ -192,6 +192,42 @@ void main() {
|
||||
.called(1);
|
||||
});
|
||||
|
||||
test('setDescription waits for initialize before calling dispose',
|
||||
() async {
|
||||
final CameraController cameraController = CameraController(
|
||||
const CameraDescription(
|
||||
name: 'cam',
|
||||
lensDirection: CameraLensDirection.back,
|
||||
sensorOrientation: 90,
|
||||
),
|
||||
ResolutionPreset.max,
|
||||
imageFormatGroup: ImageFormatGroup.bgra8888,
|
||||
);
|
||||
|
||||
final Completer<void> initializeCompleter = Completer<void>();
|
||||
when(CameraPlatform.instance.initializeCamera(
|
||||
mockInitializeCamera,
|
||||
imageFormatGroup: ImageFormatGroup.bgra8888,
|
||||
)).thenAnswer(
|
||||
(_) => initializeCompleter.future,
|
||||
);
|
||||
|
||||
unawaited(cameraController.initialize());
|
||||
|
||||
final Future<void> setDescriptionFuture = cameraController.setDescription(
|
||||
const CameraDescription(
|
||||
name: 'cam2',
|
||||
lensDirection: CameraLensDirection.front,
|
||||
sensorOrientation: 90),
|
||||
);
|
||||
verifyNever(CameraPlatform.instance.dispose(mockInitializeCamera));
|
||||
|
||||
initializeCompleter.complete();
|
||||
|
||||
await setDescriptionFuture;
|
||||
verify(CameraPlatform.instance.dispose(mockInitializeCamera));
|
||||
});
|
||||
|
||||
test('prepareForVideoRecording() calls $CameraPlatform ', () async {
|
||||
final CameraController cameraController = CameraController(
|
||||
const CameraDescription(
|
||||
|
Reference in New Issue
Block a user