SecretBit
SecretBit

Reputation: 127

Stopping CaptureCallback request

I'm trying to use the Camera2 APIs to read a QR code using Zbar. Unfortunately, I'm getting the following error when I try and finish the activity after I have successfully scanned a QR code:

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)

And these are the relevant snippets of code:

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

How do I stop the CaptureCallback from being called and cleanly return a result to my main activity?

Upvotes: 3

Views: 5935

Answers (2)

Leo Nikkilä
Leo Nikkilä

Reputation: 1557

You’re calling CameraDevice#close(), but this doesn’t close the camera immediately. You should keep the thread running until the device is done.

Your code might be based on Google’s Camera2 sample app. To finish everything off neatly, stop the thread in your CameraDevice.StateCallback instead by overriding the #onClosed(CameraDevice) method:

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

    // ...

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

};

It looks like the current behaviour might be a bug since the documentation for CameraDevice#close() says:

Immediately after this call, besides the final onClosed(CameraDevice) calls, no further callbacks from the device or the active session will occur, and any remaining submitted capture requests will be discarded, as if abortCaptures() had been called, except that no success or failure callbacks will be invoked.

Upvotes: 6

rcsumner
rcsumner

Reputation: 1663

This is probably because you changed the Camera2Basic code's mBackgroundThread.join(); to mBackgroundThread.join(1);.

Even though you invoke mCaptureSession.close(), there will likely still be some requested captures "in-flight" in the camera pipeline, so they will still appear and invoke onCaptureCompleted(...) after you close the capture session.

The original code allows these lingering calls to be resolved before closing the thread, but you are closing it after only 1ms, which is not enough time to ensure they are resolved. You should let it finish its business first.

Upvotes: 0

Related Questions