Android CameraX有时会在拍摄图像后崩溃

3df52oht  于 12个月前  发布在  Android
关注(0)|答案(1)|浏览(223)

我正在实现一个出勤功能,需要你采取当前的GPS位置和自拍.
这是简化的Replikin活动

public class CheckinActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityCheckinBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        
        binding.btnTakePicture.setOnClickListener(v -> {
            CameraActivity.launch(CheckinActivity.this, 67);
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        String photoPath = CameraActivity.getResult(resultCode, data);

        if (photoPath == null) return;
        else {
           // Display the captured image on ImageView
        }
    }
}

字符串
这是执行实际工作的Camera Activity

public class CameraActivity extends AppCompatActivity {

    private PreviewView previewView;
    private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
    ActivityCameraBinding binding;
    private int mLensFacing;
    ProcessCameraProvider processCameraProvider;

    public static void launch(Activity context) {
        context.startActivityForResult(new Intent(context, CameraActivity.class), 67);
    }

    public static void launch(Activity context, int requestCode) {
        context.startActivityForResult(new Intent(context, CameraActivity.class), requestCode);
    }

    public static String getResult(int resultCode, @Nullable Intent data) {
        if (resultCode != RESULT_OK || data == null) return null;
        return data.getStringExtra("IMG_PATH");
    }

    public static String getResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (requestCode != 67 || resultCode != RESULT_OK || data == null) return null;
        return data.getStringExtra("IMG_PATH");
    }

    @SuppressLint("RestrictedApi")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityCameraBinding.inflate(getLayoutInflater());
        previewView = binding.previewView;
        setContentView(binding.getRoot());
        cameraProviderFuture = ProcessCameraProvider.getInstance(this);
        mLensFacing = CameraSelector.DEFAULT_BACK_CAMERA.getLensFacing();

        cameraProviderFuture.addListener(new Runnable() {
            @Override
            public void run() {
                try {
                    //ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
                    processCameraProvider = cameraProviderFuture.get();
                    bindUsecase(processCameraProvider);
                } catch (ExecutionException | InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, ContextCompat.getMainExecutor(this));

        binding.btnCameraSwitch.setOnClickListener(v -> {
            switchCamera();
        });
    }

    private void switchCamera(){
        int newLensFacing;
        if (mLensFacing == CameraSelector.LENS_FACING_FRONT){
            newLensFacing = CameraSelector.LENS_FACING_BACK;
        } else {
            newLensFacing = CameraSelector.LENS_FACING_FRONT;
        }

        try {
            if (processCameraProvider.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA)) {
                mLensFacing = newLensFacing;
                bindUsecase(processCameraProvider);
                return;
            }
        }
        catch (CameraInfoUnavailableException ce){
            Toast.makeText(getApplicationContext(), "Camera is not found...", Toast.LENGTH_SHORT).show();
        }
    }

    private void bindUsecase(@NonNull ProcessCameraProvider cameraProvider){

        processCameraProvider.unbindAll();

        ImageCapture imageCapture = new ImageCapture.Builder()
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .setTargetResolution(new Size(1280, 720))
                .build();

        binding.btnCameraCapture.setOnClickListener(v -> {
            String FILENAME_FORMAT = "ddMMyyyy_HHmmss";
            ContentValues capturedContentValues = new ContentValues();
            capturedContentValues.put(MediaStore.MediaColumns.DISPLAY_NAME,
                    "CC_"+new SimpleDateFormat(FILENAME_FORMAT, Locale.US).format(System.currentTimeMillis()));
            capturedContentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM+"/CoolCompany");
            capturedContentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");

            ImageCapture.OutputFileOptions outputFileOptions1 = new ImageCapture.OutputFileOptions.Builder(getContentResolver(),
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    capturedContentValues).build();

            imageCapture.takePicture(outputFileOptions1, ContextCompat.getMainExecutor(CameraActivity.this),
                    new ImageCapture.OnImageSavedCallback() {
                        @Override
                        public void onImageSaved(@NonNull ImageCapture.OutputFileResults output) {

                            Intent data = new Intent();
                            data.putExtra("IMG_PATH", getRealPathFromURI(CameraActivity.this, output.getSavedUri()));
                            setResult(RESULT_OK, data);
                            finish();

                        }

                        @Override
                        public void onError(@NonNull ImageCaptureException exc) {
                            Toast.makeText(getApplicationContext(), "Failed to save image: "+exc.getMessage(), Toast.LENGTH_SHORT).show();
                        }
                    });
        });

        Preview preview = new Preview.Builder().build();
        CameraSelector cameraSelector = new CameraSelector.Builder()
                .requireLensFacing(mLensFacing).build();
        preview.setSurfaceProvider(previewView.getSurfaceProvider());
        cameraProvider.bindToLifecycle(((LifecycleOwner) this), cameraSelector, imageCapture, preview);
    }

    private String getRealPathFromURI(Context mContext, Uri contentUri) {
        String[] proj = { MediaStore.Images.Media.DATA };
        CursorLoader loader = new CursorLoader(mContext, contentUri, proj, null, null, null);
        Cursor cursor = loader.loadInBackground();
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        String result = cursor.getString(column_index);
        cursor.close();
        return result;
    }
}


有时,一些三星用户(运行Android 11)抱怨应用程序崩溃后采取自拍.他们指出我这些行:

Stack trace:
java.lang.IllegalStateException: Image is already closed 
at android.media.Image.throwISEIfImageIsInvalid(Image.java:71)
at android.media.ImageReader$SurfaceImage.getFormat(ImageReader.java:833)
at androidx.camera.core.AndroidImageProxy.getFormat(AndroidImageProxy.java:84)
at androidx.camera.core.ForwardingImageProxy.getFormat(ForwardingImageProxy.java:71)
at androidx.camera.core.ImageUtil.ImageToJpegByteArray(ImageUtil.java:53)
at androidx.camera.core.ImageSaver.run(ImageSaver.java:76)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1157)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)


不确定崩溃是由相机库内部的一些bug引起的,还是不正确的使用它。我使用CameraX 1.2.3,因为1.3.0和更高版本会导致Gradle错误(可能与其他库冲突),我想避免依赖地狱。

llmtgqce

llmtgqce1#

对于考勤功能,简化的Check-in活动可以捕获当前的GPS位置和自拍照。由于1.3.0及更高版本的问题,使用CameraX 1.2.3调查崩溃,以避免Gradle错误和潜在的库冲突。

相关问题