在Galaxy s6 Edge

时间:2016-06-24 13:49:00

标签: android android-camera

我在Galaxy s6边缘设备上有以下例外

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)

这是我的代码:

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());
        }
    }
}

这是活动的代码

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();
    }
}

1 个答案:

答案 0 :(得分:21)

您的代码存在严重缺陷:每个 onPause 都会调用mCamera.release(),但Camera.open()仅称为 onCreate

这就是说,在主(UI)线程上打开相机是一种不好的做法:这个调用在某些设备上可能需要很长时间(不在Samsung Galaxy 6 上),甚至导致ANR。

最佳做法是在后台HandlerThread上打开相机(请参阅https://stackoverflow.com/a/19154438/192373)。这将保证相机回调(包括onPictureTaken())不会冻结您的UI线程。

无论如何,为了使 mCamera 对象与您的活动生命周期及其 SurfaceView 保持同步,我建议您从启动Camera.open() surfaceCreated mCamera.release() - 来自 surfaceDestroyed