Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Camera is being used after Camera.release() was called in Galaxy s6 Edge

I have the following exception on Galaxy s6 edge device

java.lang.RuntimeException: Camera is being used after Camera.release() was called
at android.hardware.Camera.setPreviewSurface(Native Method)
at android.hardware.Camera.setPreviewDisplay(Camera.java:702)
at com.forsale.forsale.view.uicomponent.qrcode.CameraPreview.surfaceCreated(CameraPreview.java:59)
at android.view.SurfaceView.updateWindow(SurfaceView.java:712)
at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:316)
at android.view.View.dispatchWindowVisibilityChanged(View.java:10434)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1328)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1328)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1328)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1328)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1328)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1750)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1437)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7397)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:920)
at android.view.Choreographer.doCallbacks(Choreographer.java:695)
at android.view.Choreographer.doFrame(Choreographer.java:631)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:906)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7224)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)

this is my code :

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;
    private PreviewCallback previewCallback;
    private AutoFocusCallback autoFocusCallback;

    public CameraPreview(Context context, Camera camera,
                         PreviewCallback previewCb,
                         AutoFocusCallback autoFocusCb) {
        super(context);
        mCamera = camera;
        previewCallback = previewCb;
        autoFocusCallback = autoFocusCb;

        /* 
         * Set camera to continuous focus if supported, otherwise use
         * software auto-focus. Only works for API level >=9.
         */
        /*
        Camera.Parameters parameters = camera.getParameters();
        for (String f : parameters.getSupportedFocusModes()) {
            if (f == Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) {
                mCamera.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                autoFocusCallback = null;
                break;
            }
        }
        */

        // 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);
        } catch (IOException e) {
            Log.d("DBG", "Error setting camera preview: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // Camera preview released in activity
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        /*
         * 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
        }

        try {
            // Hard code camera surface rotation 90 degs to match Activity view in portrait
            mCamera.setDisplayOrientation(90);

            mCamera.setPreviewDisplay(mHolder);
            mCamera.setPreviewCallback(previewCallback);
            mCamera.startPreview();
            mCamera.autoFocus(autoFocusCallback);
        } catch (Exception e){
            Log.d("DBG", "Error starting camera preview: " + e.getMessage());
        }
    }
}

this is the code of the activity

public class QRCodeActivity extends BaseActivity
{
    private Camera          mCamera;
    private CameraPreview   mPreview;
    private Handler         mAutoFocusHandler;
    private ImageScanner    mScanner;

    private boolean         mBarcodeScanned = false;
    private boolean         mPreviewing = true;

    static {
        System.loadLibrary("iconv");
    }

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_qrcode);

        initializeActionBar();
        setActionBarTitle(getString(R.string.qrcode));

        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        mAutoFocusHandler = new Handler();
        mCamera = getCameraInstance();

        mScanner = new ImageScanner();
        mScanner.setConfig(0, Config.X_DENSITY, 3);
        mScanner.setConfig(0, Config.Y_DENSITY, 3);

        mPreview = new CameraPreview(this, mCamera, previewCb, autoFocusCB);
        FrameLayout preview = (FrameLayout)findViewById(R.id.cameraPreview);
        preview.addView(mPreview);
    }

    public void onPause() {

        super.onPause();
        releaseCamera();
    }

    public static Camera getCameraInstance(){

        Camera c = null;
        try {
            c = Camera.open();
        } catch (Exception e){
        }
        return c;
    }

    private void releaseCamera() {
        if (mCamera != null) {
            mPreviewing = false;
            mCamera.setPreviewCallback(null);
            mCamera.release();
            mCamera = null;
        }
    }

    private Runnable doAutoFocus = new Runnable() {

            public void run() {

                if (mPreviewing)
                    mCamera.autoFocus(autoFocusCB);
            }
        };

    PreviewCallback previewCb = new PreviewCallback() {
            public void onPreviewFrame(byte[] data, Camera camera) {
                Parameters parameters = camera.getParameters();
                Size size = parameters.getPreviewSize();

                Image barcode = new Image(size.width, size.height, "Y800");
                barcode.setData(data);

                int result = mScanner.scanImage(barcode);

                if (result != 0) {
                    mPreviewing = false;
                    mCamera.setPreviewCallback(null);
                    mCamera.stopPreview();

                    SymbolSet syms = mScanner.getResults();
                    for (Symbol sym : syms) {

                        showProgressDialog();
                        ForSaleServerManager.getInstance().verifyQRCode(QRCodeActivity.this, PhoneUtils.getDeviceId(QRCodeActivity.this) , sym.getData() , new VerifyQRCodeUIListener() {
                            @Override
                            public void onVerifyQRCodeCompleted(QRCodeResponse response, AppError error) {

                                hideProgressDialog();

                                if (error != null) {

                                    DialogUtils.showDialogMessage(
                                            QRCodeActivity.this,
                                            getString(R.string.error),
                                            PhoneUtils.getErrorMessage(QRCodeActivity.this, error),
                                            getString(R.string.ok), null);
                                } else {

                                    if (response != null && response.getError() == null) {

                                        DialogUtils.showDialogMessage(QRCodeActivity.this, getString(R.string.info),
                                                response.getMessage(), getString(R.string.ok), new View.OnClickListener() {
                                                    @Override
                                                    public void onClick(View view) {
                                                        finish();
                                                    }
                                                });
                                    } else if (response != null && response.getError() != null) {

                                        DialogUtils.showDialogMessage(
                                                QRCodeActivity.this,
                                                getString(R.string.error),
                                                response.getError().getMessage(),
                                                getString(R.string.ok), null);
                                    }
                                }
                            }
                        });

                        mBarcodeScanned = true;
                    }
                }
            }
        };

    AutoFocusCallback autoFocusCB = new AutoFocusCallback() {
            public void onAutoFocus(boolean success, Camera camera) {
                mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
            }
        };

    @Override
    protected void initializeUIComponents() {
    }

    @Override
    protected void initializeUIComponentsData() {
    }

    @Override
    protected void initializeUIComponentsTheme() {
    }

    @Override
    protected void initializeUIComponentsAction() {
    }

    @Override
    public void goodTimeToReleaseMemory() {

        /*mCamera = null;
        mPreview = null;
        mAutoFocusHandler = null;
        mScanner = null;*/

        Runtime.getRuntime().gc();
        finish();
    }
}
like image 717
Amira Elsayed Ismail Avatar asked Jun 24 '16 13:06

Amira Elsayed Ismail


1 Answers

There is a serious flaw in your code: mCamera.release() is called on every onPause, but Camera.open() is only called onCreate.

This said, it is a bad practice to open camera on the main (UI) thread: this call may take too long on some devices (not on Samsung Galaxy 6) and even cause ANR.

The best practice is to open camera on a background HandlerThread (see https://stackoverflow.com/a/19154438/192373). This will guarantee that the camera callbacks (including onPictureTaken()) don't freeze your UI thread.

At any rate, to keep the mCamera object synchronized with the lifecycle of your activity and its SurfaceView, I recommend to start Camera.open() from surfaceCreated and mCamera.release() - from surfaceDestroyed.

like image 93
Alex Cohn Avatar answered Nov 28 '22 09:11

Alex Cohn