停止CaptureCallback请求

时间:2015-04-01 23:50:10

标签: android android-camera

我正在尝试使用Camera2 API使用Zbar读取QR码。不幸的是,当我在成功扫描QR码后尝试完成活动时,我收到以下错误:

Handler (android.os.Handler) {282a7034} sending message to a Handler on a dead thread
java.lang.IllegalStateException: Handler (android.os.Handler) {282a7034} sending message to a Handler on a dead thread
    at android.os.MessageQueue.enqueueMessage(MessageQueue.java:325)
    at android.os.Handler.enqueueMessage(Handler.java:631)
    at android.os.Handler.sendMessageAtTime(Handler.java:600)
    at android.os.Handler.sendMessageDelayed(Handler.java:570)
    at android.os.Handler.post(Handler.java:326)
    at android.hardware.camera2.dispatch.HandlerDispatcher.dispatch(HandlerDispatcher.java:61)
    at android.hardware.camera2.dispatch.MethodNameInvoker.invoke(MethodNameInvoker.java:88)
    at android.hardware.camera2.dispatch.DuckTypingDispatcher.dispatch(DuckTypingDispatcher.java:53)
    at android.hardware.camera2.dispatch.ArgumentReplacingDispatcher.dispatch(ArgumentReplacingDispatcher.java:74)
    at android.hardware.camera2.dispatch.BroadcastDispatcher.dispatch(BroadcastDispatcher.java:54)
    at android.hardware.camera2.dispatch.MethodNameInvoker.invoke(MethodNameInvoker.java:88)
    at android.hardware.camera2.impl.CallbackProxies$DeviceCaptureCallbackProxy.onCaptureCompleted(CallbackProxies.java:120)
    at android.hardware.camera2.impl.CameraDeviceImpl$CameraDeviceCallbacks$4.run(CameraDeviceImpl.java:1362)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:135)
    at android.os.HandlerThread.run(HandlerThread.java:61)

这些是相关的代码片段:

private CameraCaptureSession.CaptureCallback mCaptureCallback
        = new CameraCaptureSession.CaptureCallback() {

    @Override
    public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
                                   TotalCaptureResult result) {
        Bitmap barcodeBmp = mTextureView.getBitmap();
        if(barcodeBmp == null)
            return;
        int width = barcodeBmp.getWidth();
        int height = barcodeBmp.getHeight();

        Image mQRCode = new Image(width, height, "RGB4");
        final ImageScanner scanner = new ImageScanner();
        scanner.setConfig(0, Config.X_DENSITY, 3);
        scanner.setConfig(0, Config.Y_DENSITY, 3);
        scanner.setConfig(0, Config.ENABLE, 0); //Disable all the Symbols
        scanner.setConfig(Symbol.QRCODE, Config.ENABLE, 1); //Only QRCODE is enable

        int[] pixels = new int[(width * height)];
        barcodeBmp.getPixels(pixels, 0, width, 0, 0, width, height);
        mQRCode.setData(pixels);
        int scanResult = scanner.scanImage(mQRCode.convert("Y800"));

        Log.i(TAG, "Result = " + scanResult);

        if(scanResult != 0) {

            Log.i(TAG, "Getting results");
            SymbolSet syms = scanner.getResults();
            Log.i(TAG, "Have results");
            for(Symbol sym : syms) {
                Intent returnIntent = new Intent();
                returnIntent.putExtra("Result", sym.getData());
                setResult(RESULT_OK, returnIntent);
                closeCamera();
                stopBackgroundThread();
                mActivity.finish();
            }

        }
    }

};

private void closeCamera() {
    try {
        mCameraOpenCloseLock.acquire();
        if (null != mCaptureSession) {
            mCaptureSession.close();
            mCaptureSession = null;
        }
        if (null != mCameraDevice) {
            mCameraDevice.close();
            mCameraDevice = null;
        }
        if (null != mImageReader) {
            mImageReader.close();
            mImageReader = null;
        }
    } catch (InterruptedException e) {
        throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
    } finally {
        mCameraOpenCloseLock.release();
    }
}

private void stopBackgroundThread() {
    if(mBackgroundThread != null) {
        mBackgroundThread.quitSafely();
        try {
            mBackgroundThread.join(1);
            mBackgroundThread = null;
            mBackgroundHandler = null;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

如何停止调用CaptureCallback并将结果干净地返回到我的主要活动?

2 个答案:

答案 0 :(得分:5)

您正在呼叫CameraDevice#close(),但这不会立即关闭相机。您应该保持线程运行,直到设备完成。

您的代码可能基于Google’s Camera2 sample app。要完整地完成所有操作,请通过覆盖CameraDevice.StateCallback方法停止#onClosed(CameraDevice)中的主题:

private CameraDevice.StateCallback mStateCallback
        = new CameraDevice.StateCallback() {

    // ...

    @Override
    public void onClosed(CameraDevice camera) {
        // Keep the thread alive until the camera is closed.
        stopBackgroundThread();
    }

};

看起来当前行为可能是一个错误,因为documentation for CameraDevice#close()说:

  

此次通话结束后,除了最后onClosed(CameraDevice)次来电之外,不会再发生来自设备或活动会话的回调,并且任何剩余的已提交捕获请求都将被丢弃,就好像已调用abortCaptures(),但不会调用成功或失败的回调。

答案 1 :(得分:0)

这可能是因为您将Camera2Basic代码&{39}更改为mBackgroundThread.join();

即使你调用了mBackgroundThread.join(1);,仍然可能会有一些请求的捕获"在飞行中"在相机管道中,因此在关闭捕获会话后它们仍会出现并调用mCaptureSession.close()

原始代码允许在关闭线程之前解析这些延迟调用,但是仅在1ms之后关闭它,这还不足以确保它们被解析。你应该让它先完成它的业务。