我有一个为Android 26 SDK开发的Camera App。我曾经在Motorola G5和G6上愉快地使用过它,但是当我移动到Motorola G7时,当我按下按钮在我的应用程序中拍照时,该应用程序崩溃。
G7正在运行Android9。我还有另一部Android 9手机,即Samsung S10 plus。当我按下拍照按钮时,S10 plus不会崩溃。
在调试时,我注意到G7不会调用S10时的ImageReader.OnImageAvailableListener。查看代码,这是图像的保存位置,供以后在CameraCaptureSession.CaptureCallback中使用。该回调函数期望填充字节并在不存在该字节时崩溃(我没有包括堆栈跟踪,因为它有点帮助,但是如果您认为自己愿意的话,我可以。)
如果在某些情况下通过调试缓慢运行G7,则可以保存G7。
因此,我有一个按钮,在其中调用函数onImageCaptureClick()会执行很多操作,但是它要做的一件事是创建ImageReader.OnImageAvailableListener。 OnImageAvailableListener保存图像并从图像缓冲区填充一个可变字节。通过使用reader.setOnImageAvailableListener(readerListener,null)将此onImageAvailableListener附加到我的阅读器,并且从未使用过此侦听器。当我进入CaptureCallBack时,不会填充类变量字节,并且应用程序崩溃。
您知道我要在哪里解决吗?
protected void onImageCaptureClick() {
if (null == mCameraDevice) {
logger.debug("null == mCameraDevice");
Log.e(TAG, "cameraDevice is null");
return;
}
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraDevice.getId());
Size[] jpegSizes = null;
if (characteristics != null) {
jpegSizes = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(ImageFormat.JPEG);
}
int width = 640;
int height = 480;
if (jpegSizes != null && 0 < jpegSizes.length) {
width = jpegSizes[0].getWidth();
height = jpegSizes[0].getHeight();
}
ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
List < Surface > outputSurfaces = new ArrayList < > (2);
outputSurfaces.add(reader.getSurface());
outputSurfaces.add(new Surface(mTextureView.getSurfaceTexture()));
final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(reader.getSurface());
if (mFlashMode == FLASH_MODE_OFF) {
captureBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
logger.debug("FLASH OFF");
}
if (mFlashMode == CONTROL_AE_MODE_ON) {
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
captureBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_TORCH);
logger.debug("FLASH ON");
}
if (mFlashMode == CONTROL_AE_MODE_ON_AUTO_FLASH) {
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
captureBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
logger.debug("FLASH AUTO");
}
captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);
int rotation = getWindowManager().getDefaultDisplay().getRotation();
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
final File file = new File(_pictureUri.getPath());
logger.debug("OnImageCaptureClick: _pictureUri is: " + _pictureUri.getPath());
// ************************************
// this listener is not used on the G7,
// and so the image isn't saved.
// ************************************
ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = null;
try {
image = reader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
bytes = new byte[buffer.capacity()];
buffer.get(bytes);
logger.debug("onImageCaptureClick, the filesize to save is: " + bytes.toString());
save();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (image != null) {
image.close();
}
}
}
private void save() throws IOException {
OutputStream output = null;
try {
output = new FileOutputStream(file);
output.write(bytes);
} finally {
if (null != output) {
output.close();
}
}
}
};
// ********************************************************
// the reader sets the listener here but it is never called
// and when I get in to the CaptureCallback the BitmapUtils
// expects bytes to be populated and crashes the app
// ********************************************************
reader.setOnImageAvailableListener(readerListener, null);
final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
try {
BitmapUtils.addTimeStampAndRotate(_pictureUri, bytes);
Intent intent = new Intent(CameraActivity.this, CameraReviewPhotoActivity.class);
intent.putExtra(MediaStore.EXTRA_OUTPUT, _pictureUri);
startActivityForResult(intent, CameraActivity.kRequest_Code_Approve_Image);
} catch (IOException e) {
e.printStackTrace();
} catch (ImageReadException e) {
e.printStackTrace();
} catch (ImageWriteException e) {
e.printStackTrace();
}
}
};
mCameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
try {
session.capture(captureBuilder.build(), captureListener, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
Log.w(TAG, "Failed to configure camera");
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
} finally {
takePictureButton.setEnabled(false);
mTextureView.setEnabled(false);
}
答案 0 :(得分:0)
我想我已经找到解决方案了。
我有以下电话:
我的应用程序可在S10和G6上运行。
S10和G6都在OnImageAvailableListener
回调之前调用onCaptureCompleted
函数。但是,G7分别以onCaptureCompleted
和OnImageAvailableListener
的方式调用它们。
根据https://proandroiddev.com/understanding-camera2-api-from-callbacks-part-1-5d348de65950,正确的方法是依次为onCaptureCompleted
和OnImageAvailableListener
。
在我的代码中,我假设OnImageAvailableListener
保存了图像,然后OnCaptureCompleted
尝试对其进行操作,这导致崩溃。
从每个设备的INFO_SUPPORTED_HARDWARE_LEVEL
看,我有以下级别的支持,从无级别0到超高级3。
我现在的假设是,与其他级别相比,当您支持级别3的android-camera2 API时,事件以不同的顺序触发。
希望这会有所帮助
答案 1 :(得分:0)
API不保证onCaptureCompleted和OnImageAvailableListener的顺序。它们可能以任意顺序到达,具体取决于设备,捕获设置,设备上的负载,甚至您拥有的特定操作系统版本。
请不要对此做任何假设。
相反,如果在处理某件事之前需要同时触发两个回调,则请等两个都发生后再继续前进。例如,检查每个回调中是否已触发另一个回调,如果是,则调用该方法进行处理。