Android Kotlin条码扫描CameraSource.takePicture调用失败崩溃
问题触发原因
- 重复调用拍照接口:条码检测器的
receiveDetections回调会在识别到条码的帧持续触发,未加结果拦截逻辑时,第一次识别到条码后的几十毫秒内会重复调用十几次cameraSource.takePicture()。而Android Camera1硬件同一时间仅能处理一个拍照请求,上一次拍照流程未完成时重复调用会直接触发native层崩溃,对应日志里的java.lang.RuntimeException: takePicture failed。 - 相机预览状态异常:在
surfaceChanged回调中直接调用cameraSource.start(),没有先停止已有的预览,会导致相机内部状态紊乱,进一步提升拍照失败概率。 - 拍照后未恢复预览:
CameraSource.takePicture执行完成后会自动停止相机预览,未做预览重启逻辑的话,后续即使不崩溃也无法继续扫码。
修复方案
- 新增状态标记位,防止重复触发
在MainActivity的成员变量区新增两个布尔标记:
// 标记是否已处理当前扫码结果,避免同一码重复触发逻辑 private var isBarcodeHandled = false // 标记是否正在执行拍照流程,禁止重复调用拍照接口 private var isCapturingImage = false
- 修正扫码回调逻辑,增加拦截判断
修改barcodeDetector.setProcessor内的receiveDetections方法:
override fun receiveDetections(detections: Detections<Barcode>) { val barcodes = detections.detectedItems // 不符合触发条件直接返回 if (isBarcodeHandled || isCapturingImage || barcodes.size() != 1) { return } // 先打标记,阻断后续重复回调 isBarcodeHandled = true scannedValue = barcodes.valueAt(0).rawValue runOnUiThread { Toast.makeText(this@MainActivity, "value- $scannedValue", Toast.LENGTH_SHORT).show() isCapturingImage = true cameraSource.takePicture(null) { imageBytes -> // 拍照结果回调在子线程,切主线程更新UI runOnUiThread { val bmp = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size) binding.imageViewTest.setImageBitmap( Bitmap.createScaledBitmap(bmp, binding.imageViewTest.width, binding.imageViewTest.height, false) ) isCapturingImage = false // 拍完如果需要继续扫码,执行以下逻辑重启预览;不需要就直接调用cameraSource.stop() try { cameraSource.stop() cameraSource.start(binding.cameraSurfaceView.holder) // 重置标记,支持下一次扫码识别 isBarcodeHandled = false } catch (e: IOException) { e.printStackTrace() } } } } }
- 修正Surface回调的相机启动逻辑,避免状态异常
修改SurfaceHolder.Callback的surfaceChanged实现:
@SuppressLint("MissingPermission") override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { // 先停止已有预览再重新启动,避免重复start导致状态紊乱 try { cameraSource.stop() cameraSource.start(holder) } catch (e: IOException) { e.printStackTrace() } }
扩展提示:如果需要获取仅包含条码区域的图片,可通过
barcode.boundingBox获取条码在预览帧中的坐标矩形,按照预览分辨率和拍照分辨率的比例换算坐标后,对拍照得到的Bitmap做裁剪即可。
内容的提问来源于stack exchange,提问作者Dim
相关产品推荐
相关产品推荐

