Reputation: 127
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
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 ifabortCaptures()
had been called, except that no success or failure callbacks will be invoked.
Upvotes: 6
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