应用启动首次扫码触发重复条目与Intent问题排查求助
问题修复方案
问题总结
部分设备上应用启动后首次扫码会出现重复识别(生成两条数据库记录、触发两次跳转),后续扫码恢复正常但重启后复现,同时扫码过程存在卡顿,仅特定设备表现不稳定。
核心问题定位
- 异步扫描未及时锁定处理状态,导致连续帧重复触发识别
- 每次图像分析都创建新的扫描客户端,额外增加性能开销引发卡顿
- 图像分析在主线程执行,阻塞UI导致扫码卡顿
具体修复步骤
1. 复用BarcodeScanning客户端,减少资源开销
每次processImageProxy创建新的扫描客户端会浪费资源,改为在绑定相机用例时创建并复用:
class HomeFragment : Fragment() { private var barcodeScanner: BarcodeScanner? = null // ... 原有成员变量 private fun bindCameraUseCases(cameraProvider: ProcessCameraProvider) { // ... 原有布局和扫描配置逻辑 val scannerOptions = if (binding.toggleScanner.isChecked) { // 条码格式配置 BarcodeScannerOptions.Builder() .setBarcodeFormats( Barcode.FORMAT_CODE_128, Barcode.FORMAT_CODE_39, // ... 其他条码格式 ) .build() } else { // QR码格式配置 BarcodeScannerOptions.Builder() .setBarcodeFormats(Barcode.FORMAT_QR_CODE) .build() } // 创建并保存扫描客户端 barcodeScanner = BarcodeScanning.getClient(scannerOptions) val imageAnalysis = ImageAnalysis.Builder() .setTargetRotation(binding.previewView.display?.rotation ?: Display.DEFAULT_DISPLAY) .build() // 用后台线程执行分析,避免卡顿 imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor()) { imageProxy -> processImageProxy(imageProxy) } // ... 绑定相机生命周期 } @OptIn(ExperimentalGetImage::class) private fun processImageProxy(imageProxy: ImageProxy) { Log.d("HomeFragment", "processImageProxy called with isProcessingScan: $isProcessingScan") if (isProcessingScan) { imageProxy.close() return } val mediaImage = imageProxy.image ?: run { imageProxy.close() return } val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) // 复用已创建的扫描客户端 barcodeScanner?.process(image) ?.addOnSuccessListener { barcodes -> if (barcodes.isNotEmpty()) { handleScannedResult(barcodes[0]) } else { isProcessingScan = false } } ?.addOnFailureListener { imageProxy.close() isProcessingScan = false } ?.addOnCompleteListener { imageProxy.close() } } }
2. 提前锁定扫描状态,避免重复触发
在开始处理图像帧时立即锁定状态,防止连续帧重复识别:
@OptIn(ExperimentalGetImage::class) private fun processImageProxy(imageProxy: ImageProxy) { Log.d("HomeFragment", "processImageProxy called with isProcessingScan: $isProcessingScan") if (isProcessingScan) { imageProxy.close() return } // 立即锁定状态,防止后续帧重复处理 isProcessingScan = true val mediaImage = imageProxy.image ?: run { imageProxy.close() isProcessingScan = false // 释放状态 return } val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) barcodeScanner?.process(image) ?.addOnSuccessListener { barcodes -> if (barcodes.isNotEmpty()) { handleScannedResult(barcodes[0]) } else { isProcessingScan = false } } ?.addOnFailureListener { imageProxy.close() isProcessingScan = false } ?.addOnCompleteListener { imageProxy.close() } }
同时修改handleScannedResult,移除冗余的状态设置并优化重置逻辑:
private fun handleScannedResult(barcode: Barcode) { val result = barcode.rawValue val barcodeType = barcode.format if (result != null) { if (processedBarcodes.contains(result)) { isProcessingScan = false return } processedBarcodes.add(result) // 数据库插入逻辑 val date = SimpleDateFormat("dd MMM yyyy", Locale.getDefault()).format(Date()) val time = SimpleDateFormat("hh:mm a", Locale.getDefault()).format(Date()) lifecycleScope.launch { val historyItem = HistoryItemEntity( content = result, date = date, time = time, resultType = "scanned", barcodeType = getBarcodeTypeName(barcodeType) ) AppDatabase.getDatabase(requireContext()).historyDao().insertHistoryItem(historyItem) } // 跳转结果页 val intent = Intent(requireContext(), ResultActivity::class.java).apply { putExtra("SCANNED_RESULT", result) putExtra("BARCODE_TYPE", getBarcodeTypeName(barcodeType)) } startActivity(intent) // 缩短延迟时间,避免影响后续扫码 Handler(Looper.getMainLooper()).postDelayed({ isProcessingScan = false processedBarcodes.clear() }, 2000) } else { isProcessingScan = false } }
3. 优化扫码类型切换逻辑
避免直接调用cameraProviderFuture.get()引发异常,改用监听回调:
private fun startQRScanner() { lifecycleScope.launch { cameraProviderFuture.addListener({ val cameraProvider = cameraProviderFuture.get() isProcessingScan = false processedBarcodes.clear() bindCameraUseCases(cameraProvider) }, ContextCompat.getMainExecutor(requireContext())) } } private fun startBarcodeScanner() { lifecycleScope.launch { cameraProviderFuture.addListener({ val cameraProvider = cameraProviderFuture.get() isProcessingScan = false processedBarcodes.clear() bindCameraUseCases(cameraProvider) }, ContextCompat.getMainExecutor(requireContext())) } }
修复效果说明
- 提前锁定扫描状态,从根源上阻止同一条码被连续识别
- 复用扫描客户端,减少对象创建开销,缓解扫码卡顿
- 后台线程执行图像分析,避免阻塞主线程导致的UI卡顿
- 优化状态重置逻辑,确保扫码完成或切换类型后状态正确恢复
内容的提问来源于stack exchange,提问作者Shivam
相关产品推荐
相关产品推荐

