Every time I exit the scanner camera or scan a QR code I get this warning: SurfaceView - com.example.myapp/com.example.myapp.MainActivity@a9c00b0@0#0 cancelBuffer: BufferQueue has been abandoned
My scanner QR code (all works well):
abstract class QrScannerGenericFragment : Fragment() {
// UI
private lateinit var previewView: PreviewView
private lateinit var scannerProgressBar: ProgressBar
private lateinit var scannerTextView: TextView
private lateinit var cameraProvider: ProcessCameraProvider
private lateinit var analysisUseCase: ImageAnalysis
private var rgbaBitmap: Bitmap? = null
private val cameraExecutor by lazy { Executors.newSingleThreadExecutor() }
private val scanner by lazy {
val options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_QR_CODE)
.build()
BarcodeScanning.getClient(options)
}
// HELPERS
private var scanned = false
private var infoListener: InfoListener? = null
// BINDING
private var _binding: FragmentQrScannerBinding? = null
private val binding get() = _binding!!
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is InfoListener) {
infoListener = context
} else {
throw RuntimeException("$context must implement InfoListener")
}
}
override fun onCreateView(
inflater: LayoutInflater,
parent: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentQrScannerBinding.inflate(inflater, parent, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
previewView = binding.scannerPreviewView
scannerProgressBar = binding.scannerProgressBar
scannerTextView = binding.scannerTextView
previewView.previewStreamState.observe(viewLifecycleOwner) { state ->
when (state) {
PreviewView.StreamState.IDLE -> {
scannerProgressBar.visibility = View.VISIBLE
scannerTextView.visibility = View.GONE
}
PreviewView.StreamState.STREAMING -> {
scannerProgressBar.visibility = View.GONE
scannerTextView.visibility = View.VISIBLE
}
}
}
startCamera()
}
private fun startCamera() {
scannerProgressBar.visibility = View.VISIBLE
val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
cameraProviderFuture.addListener({
cameraProvider = cameraProviderFuture.get()
val previewUseCase = Preview.Builder()
.build()
.also { it.setSurfaceProvider(previewView.surfaceProvider) }
analysisUseCase = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
.build()
.also { useCase ->
useCase.setAnalyzer(cameraExecutor) { imageProxy ->
processImageProxy(imageProxy)
}
}
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
viewLifecycleOwner,
cameraSelector,
previewUseCase,
analysisUseCase
)
}, ContextCompat.getMainExecutor(requireContext()))
}
private fun processImageProxy(imageProxy: ImageProxy) {
try {
val width = imageProxy.width
val height = imageProxy.height
if (rgbaBitmap == null
|| rgbaBitmap?.width != width
|| rgbaBitmap?.height != height
) {
rgbaBitmap?.recycle()
rgbaBitmap = createBitmap(width, height)
}
val buffer = imageProxy.planes[0].buffer
buffer.rewind()
rgbaBitmap!!.copyPixelsFromBuffer(buffer)
val inputImage =
InputImage.fromBitmap(rgbaBitmap!!, imageProxy.imageInfo.rotationDegrees)
scanner.process(inputImage)
.addOnSuccessListener { barcodes ->
if (!scanned) {
barcodes.firstOrNull()?.rawValue?.let { value ->
scanned = true
requireActivity().runOnUiThread {
onQrcodeDetected(value)
}
}
}
}
.addOnFailureListener { e ->
Log.e(LOGCAT_TAG, "Błąd skanowania", e)
}
} catch (e: Exception) {
Log.e(LOGCAT_TAG, "Error processing imageProxy", e)
} finally {
imageProxy.close()
}
}
override fun onResume() {
super.onResume()
scanned = false
}
override fun onStop() {
if (::cameraProvider.isInitialized) {
cameraProvider.unbindAll()
}
super.onStop()
}
override fun onDestroyView() {
super.onDestroyView()
if (::cameraProvider.isInitialized) {
cameraProvider.unbindAll()
}
cameraExecutor.shutdown()
scanner.close()
rgbaBitmap?.run {
if (!isRecycled) recycle()
}
rgbaBitmap = null
_binding = null
}
protected abstract fun onQrcodeDetected(value: String)
companion object {
private const val LOGCAT_TAG = "Debug"
}
}
I implement listener methods in MainActivity.kt and extends here:
class TrailsQrScannerFragment: QrScannerGenericFragment() {
private var trailsQrScannerListener: TrailsQrScannerListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)
if(context is TrailsQrScannerListener){
trailsQrScannerListener = context
}else {
throw IllegalStateException("$context must implement TrailsQrScannerListener")
}
}
override fun onQrcodeDetected(scannedText: String) {
trailsQrScannerListener?.onTrailQrcodeDetected(scannedText)
}
}
Thanks for your help!
1 Answer 1
This error might be coming because the the underlying Surface was destroyed or released before the camera instance or renderer expected it to clear. This may not be error and just a warning but you can update the code to following to prevent this error.
private var isShuttingDown = false
private fun processImageProxy(imageProxy: ImageProxy) {
if (isShuttingDown) {
imageProxy.close()
return
}
//Rest of the code
} catch (e: Exception) {
Log.e(LOGCAT_TAG, "Error processing imageProxy", e)
imageProxy.close()
}
}
Mark isShuttingDown = true before unbinding and shutting down the executor:
override fun onStop() {
isShuttingDown = true
if (::cameraProvider.isInitialized) {
cameraProvider.unbindAll()
}
super.onStop()
}
override fun onDestroyView() {
isShuttingDown = true
if (::cameraProvider.isInitialized) {
cameraProvider.unbindAll()
}
cameraExecutor.shutdownNow() // More aggressive shutdown
scanner.close()
rgbaBitmap?.run {
if (!isRecycled) recycle()
}
rgbaBitmap = null
_binding = null
super.onDestroyView()
}
1 Comment
[SurfaceView[com.my.app/com.my.app.MainActivity]#6(BLAST Consumer)6] (id:2baa00000012,api:4,p:449,c:11178) queueBuffer: BufferQueue has been abandonedExplore related questions
See similar questions with these tags.
Handler(Looper.getMainLooper()).post {navigateToCamera()}inside ofonRequestPermissionsResult()