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

使用CameraX+ML-Kit实现文字识别时首次识别后相机黑屏怎么解决?

问题根因

你的黑屏问题来自三处核心错误:

  1. 每次识别到文字后都会重新加载布局、替换页面根View,新的PreviewView没有绑定相机预览流,自然没有画面
  2. 图片识别成功后没有关闭ImageProxy,导致相机输出队列被占满,无法继续输出预览帧
  3. 布局中FrameLayout下的约束布局属性不生效,会导致文字位置不符合预期

修复步骤

1. 调整Activity代码逻辑

将ViewBinding改为全局成员,仅在onCreate中初始化一次,删除其余位置的inflate和setContentView调用,同时补全ImageProxy的释放逻辑:

class MainActivity : AppCompatActivity() {
    // 全局ViewBinding
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 仅在这里初始化一次
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        if (allPermissionsGranted()) {
            startCamera()
        } else {
            requestPermissions()
        }
    }

    private val cameraExecutor: ExecutorService by lazy { Executors.newSingleThreadExecutor() }

    override fun onDestroy() {
        super.onDestroy()
        cameraExecutor.shutdown()
    }
    private companion object {
        val TAG: String = MainActivity::class.java.simpleName
        const val REQUEST_CODE_PERMISSIONS = 10
        val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
    }
    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
    }

    private fun requestPermissions() {
        ActivityCompat.requestPermissions(
            this,
            REQUIRED_PERMISSIONS,
            REQUEST_CODE_PERMISSIONS
        )
    }
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                startCamera()
            } else {
                Toast.makeText(
                    this,
                    "Permissions not granted.",
                    Toast.LENGTH_SHORT
                ).show()
                finish()
            }
        }
    }
    class TextReaderAnalyzer(
        private val textFoundListener: (String) -> Unit
    ) : ImageAnalysis.Analyzer {


        @SuppressLint("UnsafeOptInUsageError")
        override fun analyze(imageProxy: ImageProxy) {
            imageProxy.image?.let {process(it, imageProxy) }
        }
        private fun process(image: Image, imageProxy: ImageProxy) {
            try {
                readTextFromImage(InputImage.fromMediaImage(image, 90), imageProxy)
            } catch (e: IOException) {
                Log.d(TAG, "Failed to load the image")
                e.printStackTrace()
                imageProxy.close() // 异常处释放资源
            }
        }

        @SuppressLint("UnsafeOptInUsageError")
        private fun readTextFromImage(image: InputImage, imageProxy: ImageProxy) {
            TextRecognition.getClient()
                .process(image)
                .addOnSuccessListener { visionText ->
                    processTextFromImage(visionText)
                    imageProxy.close() // 识别成功必须释放资源
                }
                .addOnFailureListener { error ->
                    Log.d(TAG, "Failed to process the image")
                    error.printStackTrace()
                    imageProxy.close()
                }
        }
        private fun processTextFromImage(visionText: Text) {
            for (block in visionText.textBlocks) {
                for (line in block.lines) {
                    for (element in line.elements) {
                        textFoundListener(element.text)
                    }
                }
            }
        }
    }
    private val imageAnalyzer by lazy {
        ImageAnalysis.Builder()
            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
            .build()
            .also {
                it.setAnalyzer(
                    cameraExecutor,
                    TextReaderAnalyzer(::onTextFound)
                )
            }
    }

    private fun onTextFound(foundText: String)  {
        // 直接用全局binding更新文字即可,不要重新加载布局
        binding.Result.text = foundText
    }

    private fun startCamera() {
        // 删掉这里的inflate和setContentView
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener(
            {
                val preview = Preview.Builder()
                    .build()
                    .also { it.setSurfaceProvider(binding.cameraPreviewView.surfaceProvider) }
                cameraProviderFuture.get().bind(preview, imageAnalyzer)
            },
            ContextCompat.getMainExecutor(this)
        )
    }

    private fun ProcessCameraProvider.bind(
        preview: Preview,
        imageAnalyzer: ImageAnalysis
    ) = try {
        unbindAll()
        bindToLifecycle(
            this@MainActivity,
            CameraSelector.DEFAULT_BACK_CAMERA,
            preview,
            imageAnalyzer
        )
    } catch (ise: IllegalStateException) {
        Log.e(TAG, "Binding failed", ise)
    }
}

2. 调整布局属性

FrameLayout不支持约束布局属性,直接用layout_gravity控制文字位置即可:

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.camera.view.PreviewView
        android:id="@+id/cameraPreviewView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <TextView
        android:id="@+id/Result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="top"
        android:paddingTop="16dp"
        android:fontFamily="sans-serif-black"
        android:textColor="@color/white"
        android:textSize="20sp"
        android:textStyle="bold"
        android:gravity="center_horizontal"/>
</FrameLayout>

修改后识别到文字时只会更新TextView的内容,不会打断相机预览流,画面就不会黑屏了。

内容的提问来源于stack exchange,提问作者UncannyFruit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 17:36:03