You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用CameraX与Android ML Kit实现扫码时出现cancelBuffer: BufferQueue has been abandoned警告的问题求助

CameraX与Android ML Kit实现扫码时出现cancelBuffer: BufferQueue has been abandoned警告的问题求助

我在用CameraX搭配ML Kit做二维码扫描功能的时候,每次退出扫描相机界面,或者扫完一个二维码之后,都会在Logcat里看到这条警告:

SurfaceView - com.example.myapp/com.example.myapp.MainActivity@a9c00b0@0#0 cancelBuffer: BufferQueue has been abandoned

虽然扫码的核心功能是正常工作的,但这个警告一直存在,我想搞清楚它的成因,以及有没有办法能消除这个警告?

以下是我的代码实现:

抽象扫描Fragment类

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"
    }
}

具体实现的扫描Fragment

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)
    }
}

我在MainActivity中实现了对应的Listener接口,整体功能运行正常,就是这个警告一直挥之不去,希望能得到大家的帮助!

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 09:19:32