使用 Jetpack 撰写的 Firebase 条形码扫描仪无法正常工作

Firebase Barcode scanner using Jetpack compose not working

正在尝试将条码扫描器迁移到 Jetpack compose 并将相机和 ML 套件依赖项更新到最新版本。

当前正确显示相机视图,但不是扫描条形码。
ImageAnalysis 分析器只运行一次。

代码

@Composable
fun CameraPreview(
    data: CameraPreviewData,
) {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current

    AndroidView(
        modifier = Modifier
            .fillMaxSize(),
        factory = { AndroidViewContext ->
            PreviewView(AndroidViewContext).apply {
                this.scaleType = PreviewView.ScaleType.FILL_CENTER
                layoutParams = ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT
                )
                // Preview is incorrectly scaled in Compose on some devices without this
                implementationMode = PreviewView.ImplementationMode.COMPATIBLE
            }
        },
        update = { previewView ->
            val cameraSelector: CameraSelector = CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                .build()
            val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
            val cameraProviderFuture: ListenableFuture<ProcessCameraProvider> =
                ProcessCameraProvider.getInstance(context)

            cameraProviderFuture.addListener({
                val preview: Preview = Preview.Builder()
                    .build()
                    .also {
                        // Attach the viewfinder's surface provider to preview use case
                        it.setSurfaceProvider(previewView.surfaceProvider)
                    }
                val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
                val barcodeAnalyser = BarcodeAnalyser { barcodes ->
                    barcodes.forEach { barcode ->
                        barcode.rawValue?.let { barcodeValue ->
                            logError("Barcode value detected: ${barcodeValue}.")
                            // Other handling code
                        }
                    }
                }
                val imageAnalysis: ImageAnalysis = ImageAnalysis.Builder()
                    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                    .build()
                    .also {
                        it.setAnalyzer(cameraExecutor, barcodeAnalyser)
                    }

                try {
                    cameraProvider.unbindAll()
                    cameraProvider.bindToLifecycle(
                        lifecycleOwner,
                        cameraSelector,
                        preview,
                        imageAnalysis
                    )
                } catch (exception: Exception) {
                    logError("Use case binding failed with exception : $exception")
                }
            }, ContextCompat.getMainExecutor(context))
        },
    )
}

条码分析器

class BarcodeAnalyser(
    private val onBarcodesDetected: (barcodes: List<Barcode>) -> Unit,
) : ImageAnalysis.Analyzer {
    private var lastAnalyzedTimestamp = 0L

    override fun analyze(
        imageProxy: ImageProxy,
    ) {
        logError("Inside analyze")
        val currentTimestamp = System.currentTimeMillis()
        if (currentTimestamp - lastAnalyzedTimestamp >= TimeUnit.SECONDS.toMillis(1)) {

            imageProxy.image?.let { imageToAnalyze ->
                val options = BarcodeScannerOptions.Builder()
                    .setBarcodeFormats(Barcode.FORMAT_ALL_FORMATS)
                    .build()
                val barcodeScanner = BarcodeScanning.getClient(options)
                val imageToProcess =
                    InputImage.fromMediaImage(imageToAnalyze, imageProxy.imageInfo.rotationDegrees)

                barcodeScanner.process(imageToProcess)
                    .addOnSuccessListener { barcodes ->
                        if (barcodes.isNotEmpty()) {
                            logError("Scanned: $barcodes")
                            onBarcodesDetected(barcodes)
                            imageProxy.close()
                        } else {
                            logError("No barcode scanned")
                        }
                    }
                    .addOnFailureListener { exception ->
                        logError("BarcodeAnalyser: Something went wrong with exception: $exception")
                        imageProxy.close()
                    }
            }
            lastAnalyzedTimestamp = currentTimestamp
        }
    }
}

参考文献

感谢 Adrian 的评论。

经过以下更改后它起作用了。

条码分析器

  1. addOnSuccessListeneraddOnFailureListener 中删除了 imageProxy.close()。将其添加到 addOnCompleteListener.
  2. 也在 else 条件中添加了 imageProxy.close()
class BarcodeAnalyser(
    private val onBarcodesDetected: (barcodes: List<Barcode>) -> Unit,
) : ImageAnalysis.Analyzer {
    private var lastAnalyzedTimestamp = 0L

    override fun analyze(
        imageProxy: ImageProxy,
    ) {
        logError("Inside analyze")
        val currentTimestamp = System.currentTimeMillis()
        if (currentTimestamp - lastAnalyzedTimestamp >= TimeUnit.SECONDS.toMillis(1)) {

            imageProxy.image?.let { imageToAnalyze ->
                // ...Same code

                barcodeScanner.process(imageToProcess)
                    .addOnSuccessListener { barcodes ->
                        if (barcodes.isNotEmpty()) {
                            logError("Scanned: $barcodes")
                            onBarcodesDetected(barcodes)
                            // imageProxy.close()
                        } else {
                            logError("No barcode scanned")
                        }
                    }
                    .addOnFailureListener { exception ->
                        logError("BarcodeAnalyser: Something went wrong with exception: $exception")
                        // imageProxy.close()
                    }
                    .addOnCompleteListener {
                        imageProxy.close()
                    }
            }
            lastAnalyzedTimestamp = currentTimestamp
        } else {
            imageProxy.close()
        }
    }
}