使用照相机包授予权限会在出现异常时使调试器暂停

时间:2019-07-12 08:37:19

标签: flutter dart camera flutter-dependencies flutter-packages

我正在使用camera软件包来实现简单的功能。我主要关注的是example提供的软件包。当我打开“相机小部件”页面时,程序包会自动提示向相机和麦克风提供权限。单击允许两个权限后,调试器将暂停,但出现以下异常:

Exception has occurred.
FlutterError (A CameraController was used after being disposed.
Once you have called dispose() on a CameraController, it can no longer be used.).

这是必需的代码:

class CameraPage extends StatefulWidget {
  @override
  _CameraPageState createState() => _CameraPageState();
}

class _CameraPageState extends State<CameraPage>
    with WidgetsBindingObserver {
  CameraController _controller;
  List<CameraDescription> _availableCameras;
  ...

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _initialize();
  }

  Future<void> _initialize() async {
    await _getCameras();
    _controller = CameraController(_availableCameras[0], ResolutionPreset.high);
    await _controller.initialize();
    if (!mounted) {
      return;
    }
    setState(() {});
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.inactive) {
      _controller?.dispose();
    } else if (state == AppLifecycleState.resumed) {
      if (_controller != null) {
        _setCurrentCamera(_controller.description);
      }
    }
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    _controller.dispose();
    super.dispose();
  }


  Future<List<CameraDescription>> _getCameras() async {
    List<CameraDescription> camDescriptions;
      camDescriptions = await availableCameras();

      _availableCameras = camDescriptions;
    return camDescriptions;
  }

  @override
  Widget build(BuildContext context) {
    ...
  }


  Future<void> _setCurrentCamera(CameraDescription cameraDescription) async {
    if (_controller != null) {
      await _controller.dispose();
    }
    _controller = CameraController(
      cameraDescription,
      ResolutionPreset.high,
      enableAudio: false,
    );

    // If the _controller is updated then update the UI.
    _controller.addListener(() {
      if (mounted) setState(() {});

      if (_controller.value.hasError) {
        print('Camera error ${_controller.value.errorDescription}');
      }
    });

    try {
      await _controller.initialize();
    } on CameraException catch (e) {
      _showCameraException(e);
    }

    if (mounted) {
      setState(() {});
    }
  }

  void _switchCamera() {
    if (_controller != null && !_controller.value.isRecordingVideo) {
      CameraLensDirection direction = _controller.description.lensDirection;
      CameraLensDirection required = direction == CameraLensDirection.front
          ? CameraLensDirection.back
          : CameraLensDirection.front;
      for (CameraDescription cameraDescription in _availableCameras) {
        if (cameraDescription.lensDirection == required) {
          _setCurrentCamera(cameraDescription);
          return;
        }
      }
    }
  }


  void _showCameraException(CameraException e) {
    String errorText = 'Error: ${e.code}\nError Message: ${e.description}';
    print(errorText);
  }

}

调试器在此处指出异常:

  Future<void> _initialize() async {
    await _getCameras();
    _controller = CameraController(_availableCameras[0], ResolutionPreset.high);
    //-------------HERE------------------
    await _controller.initialize();
    if (!mounted) {
      return;
    }
    setState(() {});
  }

一旦我恢复调试器并尝试再次打开此相机页面,就不再有错误/异常了。只有在第一次接受许可后,它才会发生。

1 个答案:

答案 0 :(得分:1)

可能的罪魁祸首是didChangeAppLifecycleState

一旦您调用await _controller.initialize();并显示权限对话框,就会触发生命周期事件AppLifecycleState.inactive并根据didChangeAppLifecycleState中的代码处理当前控制器,因此当应用程序在之后恢复时授予权限并尝试继续,它将引发错误。

尝试删除

if (state == AppLifecycleState.inactive) {
  _controller?.dispose();
}

或者有一个局部变量来检查是否初始化,并且像

一样在初始化时忽略处理
Future<void> _initialize() async {
  await _getCameras();
  _controller = CameraController(_availableCameras[0], ResolutionPreset.high);
  _initializing = true;
  await _controller.initialize();
  _initializing = false;
  if (!mounted) {
    return;
  }
  setState(() {});
}

didChangeAppLifecycleState

if (state == AppLifecycleState.inactive && !_initializing) {
  _controller?.dispose();
}

编辑:

可能是,我认为我发现了问题,实际问题是didChangeAppLifecycleState,如预期的那样,if中的didChangeAppLifecycleState子句,如果它被断言是真的,{{ 1}}正在处置,否则_controller只是在处置任何活动控制器。因此,当您调用初始化并等待权限时,在权限将来解决之前,_setCurrentCamera_controller处理。

我的解决方案可以进行简单的更改。将您的didChangeAppLifecycleState更改为

initState

将您的@override void initState() { super.initState(); _initializing = true; // set to true WidgetsBinding.instance.addObserver(this); _initialize(); } 函数更改为初始化后的_initialize

_initializing = false

和您的Future<void> _initialize() async { await _getCameras(); _controller = CameraController(_availableCameras[0],ResolutionPreset.high); await _controller.initialize(); _initializing = false; // set to false if (!mounted) { return; } setState(() {}); }

didChangeAppLifecycleState

这样,如果@override void didChangeAppLifecycleState(AppLifecycleState state) { if(_initializing){ return; } if (state == AppLifecycleState.inactive) { _controller?.dispose(); } else if (state == AppLifecycleState.resumed) { if (_controller != null) { _setCurrentCamera(_controller.description); } } } 您永远不会处置当前的控制器。


希望有帮助!