3

我正在使用相机包来实现简单的功能。我主要遵循包提供的示例。当我打开摄像头小部件页面时,软件包会自动提示提供摄像头和麦克风的权限。单击允许这两个权限后,调试器将暂停并出现异常:

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(() {});
  }

一旦我恢复调试器并尝试再次打开此相机页面,就不再有错误/异常。只有在第一次接受权限后才会发生这种情况。

4

1 回答 1

4

可能真正的罪魁祸首是didChangeAppLifecycleState

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

尝试删除

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

或者有一个局部变量来检查是否初始化并在初始化时忽略 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_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您从不处置当前控制器。


希望有帮助!

于 2019-07-15T15:42:37.833 回答