简体   繁体   中英

Android camera preview freeze when switching the camera?

This error shows " java.lang.RuntimeException: Camera is being used after Camera.release() was called" when me switching camera. Is my way to release the camera is not the correct way? Can someone suggest me the best way to implement this?

This is my 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.

And this is my 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) and Camera.open(0) return a camera instance, use it to replace mCamera . This does not explain the RuntimeException.

This exception happens because your CameraPreviewActivity keeps its separate reference to the Camera instance which you opened in CameraActivity.

The best practice would be to keep the reference only in the CameraPreviewActivity class, and remove the mCamera field from CameraActivity class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM