Сбой изображения на телефонах Samsung

Я пишу приложение для Android с помощью Xamarin, в котором есть пользовательская активность, используемая для захвата изображений с использованием API-интерфейса Camera . Эта работа отлично работает на всех устройствах, которые я тестировал, однако некоторые пользователи сообщали о сбое приложения полностью при попытке сделать снимок. Было быстро очевидно, что все эти пользователи используют телефоны Samsung, и, к сожалению, у меня нет одного теста для тестирования.

К счастью, мне удалось захватить трассировку исключения и стека, но я не понимаю, что может вызвать эту проблему. Исключение, трассировка стека и проблемный код приведены ниже.

Это довольно простая работа с полноэкранным просмотром камеры, переключателем вспышки и кнопкой захвата. Он использует собственный класс CameraHelper для настройки и взаимодействия с API-интерфейсом Camera . Камера настроена, и предварительный просмотр отображается методом OnSurfaceTextureAvailable до того, как пользователь сможет взаимодействовать и запускать метод TakePicture .

Исключительная трассировка стека

 java.lang.RuntimeException: takePicture failed android.hardware.Camera.native_takePicture(Native Method):0 android.hardware.Camera.takePicture(Camera.java:1523):0 android.hardware.Camera.takePicture(Camera.java:1468):0 md5efa7d89b8a471e1a97a183b83296df21.CameraHelper.n_onAutoFocus(Native Method):0 md5efa7d89b8a471e1a97a183b83296df21.CameraHelper.onAutoFocus(CameraHelper.java:39):0 

Методы в CameraHelper

 // Implements Camera.IPictureCallback and Camera.IAutoFocusCallback public void OnSurfaceTextureAvailable(object sender, TextureView.SurfaceTextureAvailableEventArgs e) { // Get the camera and set its orientation try { _camera = Camera.Open(_cameraInt); } catch (Exception ex) { _callback.OnInitializationFailed(ex); return; } var orientation = GetDisplayOrientation(); _camera.SetDisplayOrientation(orientation); // Set the camera parameters var cameraParameters = _camera.GetParameters(); if (cameraParameters.SupportedFocusModes != null && cameraParameters.SupportedFocusModes.Contains(Camera.Parameters.FocusModeContinuousPicture)) cameraParameters.FocusMode = Camera.Parameters.FocusModeContinuousPicture; if (cameraParameters.SupportedFlashModes != null && cameraParameters.SupportedFlashModes.Contains(Camera.Parameters.FlashModeAuto)) { cameraParameters.FlashMode = Camera.Parameters.FlashModeAuto; HasFlash = true; } cameraParameters.JpegQuality = JPEG_QUALITY; // Set the picture resolution var pictureSize = GetIdealPictureSize(cameraParameters.SupportedPictureSizes, MAX_MEGAPIXELS); _imageWidth = pictureSize.Width; _imageHeight = pictureSize.Height; cameraParameters.SetPictureSize(pictureSize.Width, pictureSize.Height); // Set the preview resolution to best match the TextureView var previewSize = GetIdealPreviewSize(cameraParameters.SupportedPreviewSizes, _previewTexture.Height, _previewTexture.Width); cameraParameters.SetPreviewSize(previewSize.Width, previewSize.Height); // Begin outputting camera preview _camera.SetParameters(cameraParameters); _camera.SetPreviewTexture(_previewTexture.SurfaceTexture); _camera.StartPreview(); UpdatePreviewTextureMatrix(); // Ensure the preview is displayed without warping // Wait for the preview EventHandler<TextureView.SurfaceTextureUpdatedEventArgs> h = null; _previewTexture.SurfaceTextureUpdated += h = (s, e2) => { _previewTexture.SurfaceTextureUpdated -= h; _callback.OnCameraPreviewReady(); _ready = true; }; } public void TakePicture() { if (!_ready || _busy) { var e = new Exception("Camera not ready"); OnTakePictureFailed(e); return; } _busy = true; _camera.AutoFocus(this); } public void OnAutoFocus(bool success, Camera camera) { try { _camera.TakePicture(null, null, this); } catch (Exception e) { // On Samsung phones the exception is always thrown here OnTakePictureFailed(e); } } public void OnPictureTaken(byte[] data, Camera camera) { _busy = false; var rotation = GetPictureRotation(); _callback.OnPictureTaken(data, rotation, _imageWidth, _imageHeight); } private void OnTakePictureFailed(Exception e) { _busy = false; _callback.OnTakePictureFailed(e); } 

Камера доступна, и предварительный просмотр отображается без проблем, и исключение распространяется только на устройства Samsung.

Исключение было вызвано тем, что телефон Samsung Galaxy не смог автоматически сфокусироваться в первый раз – в то время как большинство устройств будут только пытаться сфокусироваться один раз, телефоны Samsung Galaxy повторно пытаются выполнить три раза и вызвать обратный вызов OnAutoFocus после каждой попытки. Поскольку мой код под названием Camera.TakePicture в Camera.TakePicture вызове, он может быть вызван дважды или более быстро, поэтому он попытается сделать снимок, пока фотография уже была сделана, и выброс исключения.

Решение состояло в том, чтобы просто добавить логическое значение, которое отслеживало, был ли обратный вызов автофокусировки уже выполнен, и если это так, пропустите вызов Camera.TakePicture :

 public void OnAutoFocus(bool success, Camera camera) { if (_hasAttemptedFocus) return; else _hasAttemptedFocus = true; _camera.TakePicture(null, null, this); } public void OnPictureTaken(byte[] data, Camera camera) { _busy = _hasAttemptedFocus = false; // Do something with the image }