Skip to content

[camera] [android]CameraX doesn't release resources (DeviceOrientation) #166529

@steeveone

Description

@steeveone

Steps to reproduce

render a WebcamScreenshot("") than exit the app

Dependencies from pubspec.lock:

camera:
    dependency: "direct main"
    description:
      name: camera
      sha256: "413d2b34fe28496c35c69ede5b232fb9dd5ca2c3a4cb606b14efc1c7546cc8cb"
      url: "https://pub.dev"
    source: hosted
    version: "0.11.1"
  camera_android_camerax:
    dependency: transitive
    description:
      name: camera_android_camerax
      sha256: "13784f539c7f104766bff84e4479a70f03b29d78b208278be45c939250d9d7f5"
      url: "https://pub.dev"
    source: hosted
    version: "0.6.14+1"
  image:
    dependency: "direct main"
    description:
      name: image
      sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
      url: "https://pub.dev"
    source: hosted
    version: "4.5.4"

Expected results

No exceptions or warnings

Actual results

I get a leaked IntentReceiver io.flutter.plugins.camerax.DeviceOrientationManager when i close the app

Code sample

Code sample
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:image/image.dart' as img;

void main() {
WidgetsFlutterBinding.ensureInitialized();
  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: WebcamScreenshot(codiceFiscale: ""));
  }
}

class WebcamScreenshot extends StatefulWidget {
  final String codiceFiscale;

  const WebcamScreenshot({Key? key, required this.codiceFiscale})
    : super(key: key);
  @override
  _WebcamScreenshotState createState() => _WebcamScreenshotState();
}

class _WebcamScreenshotState extends State<WebcamScreenshot> {
  CameraController? _controller;
  List<CameraDescription>? _cameras;
  Uint8List? _imageBytes; // Memorizza l'immagine in memoria
  //ApiService apiService = ApiService();
  @override
  void initState() {
    super.initState();
    _initCamera();
  }

  Future<void> _initCamera() async {
    _cameras = await availableCameras();
    _controller = CameraController(_cameras!.last, ResolutionPreset.medium);

    await _controller!.initialize();
    if (!mounted) return;

    setState(() {}); // Aggiorna la UI dopo l'inizializzazione
  }

  Uint8List? _cropCenterSquare(Uint8List imageBytes) {
    img.Image? image = img.decodeImage(
      imageBytes,
    ); // Decodifica in formato gestibile
    if (image == null) return null;

    int size =
        image.width < image.height
            ? image.width
            : image.height; // Lato del quadrato
    int offsetX = (image.width - size) ~/ 2;
    int offsetY = (image.height - size) ~/ 2;

    img.Image cropped = img.copyCrop(
      image,
      x: offsetX,
      y: offsetY,
      width: size,
      height: size,
    );
    return Uint8List.fromList(
      img.encodePng(cropped),
    ); // Converte di nuovo in Uint8List
  }

  Future<void> _savePicture() async {
    if (_imageBytes != null) {
      //await apiService.uploadImage(_imageBytes!, widget.codiceFiscale);
    }
    Navigator.pop(context);
  }

  Future<void> _takeScreenshot() async {
    if (_controller == null || !_controller!.value.isInitialized) return;

    try {
      XFile picture = await _controller!.takePicture();
      Uint8List bytes = await picture.readAsBytes(); // Legge i byte in memoria
      Uint8List? cropped = _cropCenterSquare(bytes);
      _controller!.dispose();
      setState(() {
        if (cropped != null) {
          _imageBytes = cropped;
        } else {
          _imageBytes = bytes;
        }
      });
    } catch (e) {
      print("Errore durante lo screenshot: $e");
    }
  }

  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Webcam Screenshot")),
      body: SingleChildScrollView(
        child: Column(
          children: [
            if (_imageBytes == null)
              Padding(
                padding: const EdgeInsets.all(8.0),
                child:
                    _controller == null || !_controller!.value.isInitialized
                        ? Center(child: CircularProgressIndicator())
                        : Stack(
                          children: [
                            CameraPreview(_controller!),
                            Positioned.fill(
                              child: CustomPaint(painter: MaskPainter()),
                            ),
                          ],
                        ),
              ),
            if (_imageBytes != null)
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Center(
                  child: Image.memory(_imageBytes!),
                ), // Mostra l'immagine dalla memoria
              ),
            ElevatedButton(
              onPressed: _imageBytes == null ? _takeScreenshot : _savePicture,
              child:
                  _imageBytes == null
                      ? Text("Scatta Screenshot")
                      : Text("Salva Immagine"),
            ),
            if (_imageBytes != null)
              ElevatedButton(
                onPressed:
                    () => {
                      _initCamera().then(
                        (value) => {
                          setState(() {
                            _imageBytes = null;
                          }),
                        },
                      ),
                    },
                child: Text("rifai"),
              ),
          ],
        ),
      ),
    );
  }
}

class MaskPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    double squareSize = size.width * 0.6; // Dimensione del quadrato
    double left = (size.width - squareSize) / 2;
    double top = (size.height - squareSize) / 2;

    Paint paint = Paint()..color = Color.fromRGBO(0, 0, 0, 0.5);
    Path path =
        Path()
          ..fillType =
              PathFillType
                  .evenOdd // 🔥 Chiave per il ritaglio corretto!
          ..addRect(Rect.fromLTWH(0, 0, size.width, size.height)) // Sfondo nero
          ..addRect(
            Rect.fromLTWH(left, top, squareSize, squareSize),
          ); // Foro centrale

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
E/ActivityThread(19939): android.app.IntentReceiverLeaked: Activity com.example.bug.MainActivity has leaked IntentReceiver io.flutter.plugins.camerax.DeviceOrientationManager$1@7aee2ee that was originally registered here. Are you missing a call to unregisterReceiver()?
E/ActivityThread(19939): 	at android.app.LoadedApk$ReceiverDispatcher.<init>(LoadedApk.java:1980)
E/ActivityThread(19939): 	at android.app.LoadedApk.getReceiverDispatcher(LoadedApk.java:1690)
E/ActivityThread(19939): 	at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1901)
E/ActivityThread(19939): 	at android.app.ContextImpl.registerReceiver(ContextImpl.java:1853)
E/ActivityThread(19939): 	at android.app.ContextImpl.registerReceiver(ContextImpl.java:1841)
E/ActivityThread(19939): 	at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:772)
E/ActivityThread(19939): 	at io.flutter.plugins.camerax.DeviceOrientationManager.start(DeviceOrientationManager.java:73)
E/ActivityThread(19939): 	at io.flutter.plugins.camerax.DeviceOrientationManagerHostApiImpl.startListeningForDeviceOrientationChange(DeviceOrientationManagerHostApiImpl.java:65)
E/ActivityThread(19939): 	at io.flutter.plugins.camerax.GeneratedCameraXLibrary$DeviceOrientationManagerHostApi$-CC.lambda$setup$0(GeneratedCameraXLibrary.java:1580)
E/ActivityThread(19939): 	at io.flutter.plugins.camerax.GeneratedCameraXLibrary$DeviceOrientationManagerHostApi$$ExternalSyntheticLambda0.onMessage(D8$$SyntheticClass:0)
E/ActivityThread(19939): 	at io.flutter.plugin.common.BasicMessageChannel$IncomingMessageHandler.onMessage(BasicMessageChannel.java:261)
E/ActivityThread(19939): 	at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:292)
E/ActivityThread(19939): 	at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:319)
E/ActivityThread(19939): 	at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
E/ActivityThread(19939): 	at android.os.Handler.handleCallback(Handler.java:958)
E/ActivityThread(19939): 	at android.os.Handler.dispatchMessage(Handler.java:99)
E/ActivityThread(19939): 	at android.os.Looper.loopOnce(Looper.java:230)
E/ActivityThread(19939): 	at android.os.Looper.loop(Looper.java:319)
E/ActivityThread(19939): 	at android.app.ActivityThread.main(ActivityThread.java:9063)
E/ActivityThread(19939): 	at java.lang.reflect.Method.invoke(Native Method)
E/ActivityThread(19939): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:588)
E/ActivityThread(19939): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)

Flutter Doctor output

Doctor output
[√] Flutter (Channel stable, 3.29.2, on Microsoft Windows [Versione 10.0.26100.3624], locale it-IT) [343ms]
    • Flutter version 3.29.2 on channel stable at C:\Users\steev\Documents\flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision c236373904 (3 weeks ago), 2025-03-13 16:17:06 -0400
    • Engine revision 18b71d647a
    • Dart version 3.7.2
    • DevTools version 2.42.3

[√] Windows Version (11 Home 64-bit, 24H2, 2009) [5,3s]

[√] Android toolchain - develop for Android devices (Android SDK version 35.0.1) [7,2s]
    • Android SDK at C:\Users\steev\AppData\Local\Android\sdk
    • Platform android-35, build-tools 35.0.1
    • Java binary at: C:\Program Files\Android\Android Studio\jbr\bin\java
      This is the JDK bundled with the latest Android Studio installation on this machine.
      To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 21.0.5+-12932927-b750.29)
    • All Android licenses accepted.

[√] Chrome - develop for the web [14ms]
    • Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe

[X] Visual Studio - develop Windows apps [13ms]
    X Visual Studio not installed; this is necessary to develop Windows apps.
      Download at https://visualstudio.microsoft.com/downloads/.
      Please install the "Desktop development with C++" workload, including all of its default components

[√] Android Studio (version 2024.3) [11ms]
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 21.0.5+-12932927-b750.29)

[√] VS Code (version 1.98.2) [10ms]
    • VS Code at C:\Users\steev\AppData\Local\Programs\Microsoft VS Code
    • Flutter extension version 3.108.0

[√] Connected device (5 available) [1.644ms]
    • SM S901B (mobile)            • 192.168.1.55:38845                           • android-arm64  • Android 14 (API 34)
    • SM S901B (wireless) (mobile) • adb-RFCT7118NZN-Nu1yXK._adb-tls-connect._tcp • android-arm64  • Android 14 (API 34)
    • Windows (desktop)            • windows                                      • windows-x64    • Microsoft Windows [Versione 10.0.26100.3624]     
    • Chrome (web)                 • chrome                                       • web-javascript • Google Chrome 134.0.6998.178
    • Edge (web)                   • edge                                         • web-javascript • Microsoft Edge 134.0.3124.93

[√] Network resources [567ms]
    • All expected network resources are available.

! Doctor found issues in 1 category.

Metadata

Metadata

Labels

P2Important issues not at the top of the work listfound in release: 3.29Found to occur in 3.29found in release: 3.31Found to occur in 3.31p: cameraThe camera pluginpackageflutter/packages repository. See also p: labels.platform-androidAndroid applications specificallyteam-androidOwned by Android platform team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions