Android 切换相机时相机预览卡住?

Android camera preview freeze when switching the camera?

当我切换相机时,此错误显示“java.lang.RuntimeException: 调用 Camera.release() 后正在使用相机”。我释放相机的方式不正确吗?有人可以建议我实现这个的最佳方法吗?

这是我的CameraActivity.java:

private Camera mCamera;
private CameraPreviewActivity mPreview;
private int cameraId = 0;

private static final int MY_PERMISSIONS_REQUEST_CAMERA = 1;


/**
 * Check if this device has a camera
 */
private boolean checkCameraHardware(Context context) {
    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
        // this device has a camera
        return true;
    } else {
        // no camera on this device
        return false;
    }
}

/**
 * A safe way to get an instance of the Camera object.
 */

// attempt to get a Camera instance
public static Camera getCameraInstance() {
    Camera c = null;

    try {
        c = Camera.open();
    } catch (Exception e) {
        System.out.println("Camera not working.");
    }
    return c; // returns null if camera is unavailable
}



@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.camerapreview_activity);

    // Create our Preview view and set it as the content of our activity.
    // Create an instance of Camera

    switchCamera();
    showCamera();
}

public void switchCamera() {
    ToggleButton facingSwitch = (ToggleButton) findViewById(R.id.facingSwitch);
        facingSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
            onDestroy();
            preview.removeAllViews();
            if (isChecked) {
                // The toggle is enabled
                preview.addView(mPreview);
                Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
            } else {
                // The toggle is disabled
                preview.addView(mPreview);
                Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
            }

        }
    });
}

protected void onDestroy () {
super.onDestroy();
    if (mCamera != null){
        mCamera.stopPreview();
        mCamera.release();        // release the camera for other applications
        mCamera = null;
    }
}

public void showCamera() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {

            // Permission is not granted
            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.CAMERA)) {
                Toast.makeText(this, "Camera permission is needed to show the camera preview", Toast.LENGTH_SHORT).show();

                // Show an explanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
            } else {

                // No explanation needed; request the permission
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.CAMERA},
                        MY_PERMISSIONS_REQUEST_CAMERA);

                // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
                // app-defined int constant. The callback method gets the
                // result of the request.
            }
        }
        else {
        // Permission has already been granted
        mCamera = getCameraInstance();
        mPreview = new CameraPreviewActivity(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
    }
}

// Here, thisActivity is the current activity
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {

    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_CAMERA: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Create our Preview view and set it as the content of our activity.

                // permission was granted, yay! Do the
                // contacts-related task you need to do.
                showCamera();

            } else {
                Toast.makeText(this, "Permission was not granted", Toast.LENGTH_SHORT).show();
                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }
    }
}
        // other 'case' lines to check for other
        // permissions this app might request.

这是我的CameraPreviewActivity.java

    private SurfaceHolder mHolder;
    private Camera mCamera;

    public CameraPreviewActivity(Context context, Camera camera) {
        super(context);
        mCamera = camera;

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        if (mHolder.getSurface() == null){
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e){
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }
}

Camera.open(1)和Camera.open(0)return一个相机实例,用它来代替mCamera。这不解释 RuntimeException。

发生此异常是因为您的 CameraPreviewActivity 保留了对您在 CameraActivity 中打开的 Camera 实例的单独引用。

最佳做法是仅在 CameraPreviewActivity class 中保留引用,并从 CameraActivity class 中删除 mCamera 字段。