使用 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 的评论。
经过以下更改后它起作用了。
在条码分析器
- 从
addOnSuccessListener
和 addOnFailureListener
中删除了 imageProxy.close()
。将其添加到 addOnCompleteListener
.
- 也在 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()
}
}
}
正在尝试将条码扫描器迁移到 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 的评论。
经过以下更改后它起作用了。
在条码分析器
- 从
addOnSuccessListener
和addOnFailureListener
中删除了imageProxy.close()
。将其添加到addOnCompleteListener
. - 也在 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()
}
}
}