You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 12:57:11