通过相机 API 2 捕获图片并将其另存为位图



我正在开发一个使用相机拍照然后在另一个活动中显示它的应用程序,我尝试了以下代码

public void onImageAvailable(ImageReader imageReader) {
Image image = imageReader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
Bitmap myBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null);
SetBitmap setBitmap = new SetBitmap();
setBitmap.setMbitmap(myBitmap);
image.close();
}

在此之后,我在像这样的其他活动中获取我的位图

myBitmap = CamActivity.setBitmap.getMbitmap();
ImageView myImage = (ImageView) findViewById(R.id.imageView);
myImage.setImageBitmap(myBitmap);

设置位图是一个设置位图的类

当我单击它时,我有一个按钮可以捕获凸轮,它应该拍照并开始另一个活动来显示图像,但它随后崩溃了 那么,现在我该怎么办,我以错误的方式开始我的活动?或者问题出在位图上? 这是错误日志

   java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.android.camtest/com.example.android.camtest.DisplayActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.graphics.Bitmap com.example.android.camtest.SetBitmap.getMbitmap()' on a null object reference
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2693)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2758)
       at android.app.ActivityThread.access$900(ActivityThread.java:177)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1448)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:145)
       at android.app.ActivityThread.main(ActivityThread.java:5942)
       at java.lang.reflect.Method.invoke(Native Method)
       at java.lang.reflect.Method.invoke(Method.java:372)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)
    Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.graphics.Bitmap com.example.android.camtest.SetBitmap.getMbitmap()' on a null object reference
       at com.example.android.camtest.DisplayActivity.onCreate(DisplayActivity.java:37)
       at android.app.Activity.performCreate(Activity.java:6283)
       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2758) 
       at android.app.ActivityThread.access$900(ActivityThread.java:177) 
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1448) 
       at android.os.Handler.dispatchMessage(Handler.java:102) 
       at android.os.Looper.loop(Looper.java:145) 
       at android.app.ActivityThread.main(ActivityThread.java:5942) 
       at java.lang.reflect.Method.invoke(Native Method) 
       at java.lang.reflect.Method.invoke(Method.java:372) 
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400) 
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195) 
 java.lang.IllegalStateException: Handler (android.media.ImageReader$ListenerHandler) {b9cb033} sending message to a Handler on a dead thread
     at android.os.MessageQueue.enqueueMessage(MessageQueue.java:325)
     at android.os.Handler.enqueueMessage(Handler.java:631)
     at android.os.Handler.sendMessageAtTime(Handler.java:600)
     at android.os.Handler.sendMessageDelayed(Handler.java:570)
     at android.os.Handler.sendEmptyMessageDelayed(Handler.java:534)
     at android.os.Handler.sendEmptyMessage(Handler.java:519)
     at android.media.ImageReader.postEventFromNative(ImageReader.java:511)
     at android.hardware.camera2.legacy.LegacyCameraDevice.nativeProduceFrame(Native Method)
     at android.hardware.camera2.legacy.LegacyCameraDevice.produceFrame(LegacyCameraDevice.java:516)
     at android.hardware.camera2.legacy.RequestThreadManager$2.onPictureTaken(RequestThreadManager.java:224)
     at android.hardware.Camera$EventHandler.handleMessage(Camera.java:1142)
     at android.os.Handler.dispatchMessage(Handler.java:102)
     at android.os.Looper.loop(Looper.java:145)
     at android.hardware.camera2.legacy.CameraDeviceUserShim$CameraLooper.run(CameraDeviceUserShim.java:144)
     at java.lang.Thread.run(Thread.java:818)

这是我拍照的地方

private void takePic() {
if (cameraDevice == null)
return;
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraDevice.getId());
Size[] jpegSizes = null;
if (characteristics != null)
jpegSizes = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
.getOutputSizes(ImageFormat.JPEG);
int width = 700;
int height = 600;
if (jpegSizes != null && jpegSizes.length > 0) {
width = jpegSizes[0].getWidth();
height = jpegSizes[0].getHeight();
}
final ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
List<Surface> outputSurface = new ArrayList<>(2);
outputSurface.add(reader.getSurface());
outputSurface.add(new Surface(textureView.getSurfaceTexture()));
final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(reader.getSurface());
captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
int rotation = getWindowManager().getDefaultDisplay().getRotation();
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));

String fileName = "IMG_" + new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()) + ".jpg";
File captureDirectory = new File("/Download");
if (!captureDirectory.isDirectory()) captureDirectory.mkdirs();
final File mediaFile = new File(captureDirectory, fileName);
final Uri fileUri = FileProvider.getUriForFile(this,
"com.mydomain.fileprovider",
mediaFile);

ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader imageReader) {
Image mImage = imageReader.acquireLatestImage();
ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
try {
save(bytes);
} catch (IOException e) {
e.printStackTrace();
}
OutputStream output = null;
try {
output = getContentResolver().openOutputStream(fileUri);
output.write(bytes);
} catch (IOException e) {
e.printStackTrace();
} finally {
mImage.close();
if (output != null) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

}
private void save(byte[] bytes) throws IOException {
OutputStream output = null;
try {
output = new FileOutputStream(mediaFile);
output.write(bytes);
} finally {
if (output != null)
output.close();
}
}
};
Intent displayIntent = new Intent(this, DisplayActivity.class);
displayIntent.putExtra("FILE_URI", fileUri);
Log.e("uri", fileUri + "");

startActivity(displayIntent);
reader.setOnImageAvailableListener(readerListener, mBackgroundHandler);
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);
Toast.makeText(CamActivity.this, "Saved ", Toast.LENGTH_SHORT).show();
}
};

} catch (CameraAccessException e) {
e.printStackTrace();
}
}

这是显示活动

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display);
imageView = findViewById(R.id.imageView);
bundleExtras = getIntent().getExtras();
if(bundleExtras != null){
Uri savedBitmapUri = bundleExtras.getParcelable("FILE_URI");
imageView.setImageURI(savedBitmapUri);
Log.e("uri",savedBitmapUri+"");
} else {
Toast.makeText(this, "null pic", Toast.LENGTH_SHORT).show();
}
}

文件提供程序

<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="myDownloads" path="Download" />
<root-path name="sdcard1" path="." /> </paths>

将位图数据传递给另一个活动的方法错误。首先,您应该检查Android活动生命周期。当你启动不同的活动时,让我们从A称它为B,A活动将被销毁,所有资源将被销毁。在Android Activity LifeCycle文档中,他们提到了这一点:

onDestroy() 回调释放所有尚未释放的资源 由早期的回调(如 onStop() )释放。

因此,在活动销毁后,您将无法访问变量。你应该做的是,你应该将位图保存到文件中,并通过 intent extra 传递文件 URI,并从另一个活动的 onCreate 回调中获取 URI。

您可以使用 FileProvider 保存文件,并通过 intent 传递提供程序 URI,这是最好的">Android 类型"方法,或者您可以将文件的路径保存到共享首选项,并从另一个不稳定且不是最佳实践的活动读取它。

让我们使用 FileProvider: 您可以查看有关 FileProvider 的 Android 文档来定义它。

// Assume that you added Captures path to your FileProvider's paths.
// Create temporary image file.
String fileName = "IMG_" + new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()) + ".jpg";  
File captureDirectory = new File(getFilesDir(), "Captures");
if( !captureDirectory.isDirectory() ) captureDirectory.mkdirs();
File mediaFile = new File(captureDirectory, fileName);
Uri fileUri = FileProvider.getUriForFile(this,
"com.mydomain.fileprovider",
mediaFile);  
// Save the Bitmap.
public void onImageAvailable(ImageReader imageReader) {
Image mImage = imageReader.acquireLatestImage();
ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
OutputStream output = null;
try {
output = getContentResolver().openOutputStream(fileUri);
output.write(bytes);
} catch (IOException e) {
e.printStackTrace();
} finally {
mImage.close();
if (output != null) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// Pass the file uri via intent.
Intent displayIntent = new Intent(this, DisplayActivity.class);
displayIntent.putExtra("FILE_URI", mediaFile);
startActivity(displayIntent);
// Read the uri from started activity and show the bitmap
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display);
... 
bundleExtras = getIntent().getExtras();
if(bundleExtras != null){
Uri savedBitmapUri = bundleExtras.getParcelable("FILE_URI");
mImageView_Preview.setImageURI(savedBitmapUri);
}
else{
// Uri not inside the intent data.
}
...
}

不应将所有字节作为位图加载到内存中。只需将捕获的图像保存到文件中,并传递从应用程序的 FileProvider 创建的文件 Uri,并使用一行代码显示文件 Uri。

基本优化: - 您可以在缓存目录中创建文件
- 您可以在使用 FileProvider 完成任务后删除该文件 权限。 - 您可以在后台线程上执行保存工作,就像Google在android-Camera2Basic示例中所做的那样。 - 还有更多...

最新更新