CameraX结合ML Kit条码扫描首次识别后无法停止 重复插入Room问题
问题背景
使用CameraX结合ML Kit实现条码扫描,需要获取条码的rawValue与format两个属性,运行时出现两个异常:
- 首次检测到条码后无法终止扫描流程
- Room数据库出现重复插入问题:扫描过程中检测到多少次条码就会插入多少条记录,同一条码会被多次写入,最终产生随机数量的重复数据
问题原因
CameraX的ImageAnalysis用例会持续向ML Kit扫描器输送相机帧,预览状态下摄像头每秒输出20~30帧画面,只要条码停留在取景范围内,每一帧都会触发扫描成功回调。原有代码既没有做单次扫描的状态拦截,也没有在识别到有效条码后停止扫描流程,因此同一条码会被多次识别、多次触发数据库插入和页面跳转逻辑,最终产生重复记录。
原有问题代码
图像分析回调中的ML Kit扫描逻辑:
scanner.process(inputImage).addOnSuccessListener { barcodeList -> processBarcode(barcodeList) }
条码处理与数据存储逻辑:
private fun processBarcode(barcodeList: List<Barcode>) { if (barcodeList.isNotEmpty()) { with (barcodeList.first()) { activityCameraScannerViewModel.rawValue = this.rawValue.toString() activityCameraScannerViewModel.format = this.format.toString() activityCameraScannerViewModel.setNewCard() val intent = Intent(applicationContext, MainActivity::class.java) intent.putExtra("rawValue", this.rawValue.toString()) intent.putExtra("format", this.format.toString()) startActivity(intent) } } }
解决思路
从状态拦截、扫描终止、数据库兜底三个层面处理,彻底解决重复问题:
- 增加扫描状态标记位
在承载扫描逻辑的Activity/Fragment中定义布尔类型成员变量private var isScanning = true,每次进入条码处理逻辑首先判断该标记,只有标记为true时才执行后续存储、跳转逻辑,进入逻辑后立刻将标记设为false,从入口阻断重复触发。 - 识别到有效条码后立刻终止扫描
拿到第一条有效条码、完成状态锁定后,立刻调用CameraX的解绑方法停止图像分析,不再向ML Kit输送后续帧数据,从根源停止后续扫描回调:
如果后续需要重新开启扫描,重新绑定// 解绑图像分析用例,终止扫描流程 cameraProvider.unbind(imageAnalysis)ImageAnalysis用例、将isScanning标记重置为true即可。 - Room层增加唯一索引兜底
给条码表对应Entity中存储rawValue的字段添加唯一索引,插入数据时使用忽略冲突策略,就算上层逻辑出现漏判,也不会出现同一条码重复入库的问题:@Entity( tableName = "barcode_table", indices = [Index(value = ["rawValue"], unique = true)] ) data class BarcodeEntity( // 其他字段... val rawValue: String, val format: String ) // DAO层插入方法指定冲突策略 @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insertBarcode(barcode: BarcodeEntity)
修改后的processBarcode参考实现:
// 类成员位置定义状态标记,初始化时为扫描中状态 private var isScanning = true private fun processBarcode(barcodeList: List<Barcode>) { // 非扫描状态、或者未识别到条码直接返回 if (!isScanning || barcodeList.isEmpty()) return val targetBarcode = barcodeList.first() // 拿到有效条码立刻锁定状态,阻止后续帧重复进入逻辑 isScanning = false // 立刻解绑图像分析用例,停止接收后续相机帧 cameraProvider.unbind(imageAnalysis) // 执行原有业务逻辑 activityCameraScannerViewModel.rawValue = targetBarcode.rawValue.toString() activityCameraScannerViewModel.format = targetBarcode.format.toString() activityCameraScannerViewModel.setNewCard() val intent = Intent(applicationContext, MainActivity::class.java) intent.putExtra("rawValue", targetBarcode.rawValue.toString()) intent.putExtra("format", targetBarcode.format.toString()) startActivity(intent) }
注意:页面销毁、退出扫描页时要及时调用
scanner.close()释放ML Kit资源,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Thatta Systems
相关产品推荐
相关产品推荐

