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

Granting permission using camera package pauses the debugger on an exception

我正在使用 camera package for a simple functionality. I am mostly following the 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(() {});
  }

一旦我恢复调试器并尝试再次打开此相机页面,就没有 error/exception 了。只有在第一次接受权限后才会发生。

可能真正的罪魁祸首是 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 和预期的一样, didChangeAppLifecycleState 中的 if 子句,如果结果是真的, _controller 正在处理,如果没有,_setCurrentCamera 只是处理任何活动的控制器。因此,当您调用初始化并等待权限时,在权限未来解决之前,_controllerdidChangeAppLifecycleState.

处理

我的解决方案只需简单更改即可。将您的 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);
    }
  }
}

这样,如果 _initializing == true 你永远不会处理当前控制器。


希望对您有所帮助!