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

开启相机权限触发IllegalStateException:setCurrentState需在主线程调用

问题解决:权限授权后Activity自动返回+setCurrentState主线程异常

问题核心分析

你遇到的两个问题是关联的:

  • 权限授权后自动返回上一页:本质是IllegalStateException触发Activity崩溃,系统自动回到栈内上一个页面
  • setCurrentState must be called on the main thread异常:说明相机组件内部的setCurrentState方法被在子线程调用,违反Android UI操作必须在主线程的规则

解决方案

1. 确保相机操作严格在主线程执行+避免重复初始化

通过标记位避免重复设置相机监听,同时显式包裹所有相机组件操作,确保在主线程执行:

class KameraActivity : AppCompatActivity() {

    private lateinit var classifier: ClassificationFromCamera
    private lateinit var binding: ActivityKameraBinding
    private var isCameraSetup = false // 标记相机是否已完成初始化

    private val executor = ThreadPoolExecutor(
        2, 
        2,
        0L, 
        TimeUnit.MILLISECONDS, 
        LinkedBlockingQueue() 
    )

    @SuppressLint("MissingPermission")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityKameraBinding.inflate(layoutInflater)
        setContentView(binding.root)

        classifier = ClassificationFromCamera(assets)

        if (hasCameraPermission()) {
            setupCamera()
        } else {
            requestCameraPermissions()
        }
    }

    private fun requestCameraPermissions() {
        ActivityCompat.requestPermissions(
            this,
            arrayOf(Manifest.permission.CAMERA),
            REQUEST_CAMERA_CODE
        )
    }

    @SuppressLint("MissingPermission", "SetTextI18n")
    private fun setupCamera() {
        if (isCameraSetup) return // 避免重复初始化导致状态混乱
        isCameraSetup = true

        runOnUiThread {
            binding.camera.addPictureTakenListener { imageData ->
                executor.execute {
                    val recognitions = classifier.recognize(imageData.data)
                    val txt = recognitions.joinToString(separator = "\n\n\n\n")

                    runOnUiThread {
                        binding.tvHasil1.text = txt
                    }
                }
            }
            binding.capturePhoto.setOnClickListener {
                binding.camera.capture()
            }
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)

        if (requestCode == REQUEST_CAMERA_CODE) {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                runOnUiThread {
                    setupCamera()
                }
            } else {
                runOnUiThread {
                    Toast.makeText(
                        this,
                        "Aplikasi perlu izin penggunaan kamera",
                        Toast.LENGTH_LONG
                    ).show()
                    requestCameraPermissions()
                }
            }
        }
    }

    @SuppressLint("MissingPermission")
    override fun onResume() {
        super.onResume()
        if (hasCameraPermission() && isCameraSetup) {
            runOnUiThread {
                binding.camera.start()
            }
        }
    }

    override fun onPause() {
        if (hasCameraPermission() && isCameraSetup) {
            runOnUiThread {
                binding.camera.stop()
            }
        }
        super.onPause()
    }

    override fun onDestroy() {
        if (hasCameraPermission() && isCameraSetup) {
            runOnUiThread {
                binding.camera.destroy()
            }
        }
        super.onDestroy()
    }

    private fun hasCameraPermission() =
        ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.CAMERA
        ) == PackageManager.PERMISSION_GRANTED

    companion object {
        private const val REQUEST_CAMERA_CODE = 1
    }
}

2. 修复相机组件内部的线程问题(关键)

如果上述修改后仍报错,说明问题出在binding.camera组件内部:该组件的setCurrentState方法被在子线程调用。需要调整组件代码:

找到组件中调用setCurrentState的位置,确保切换到主线程执行,示例如下:

// 相机组件内部错误写法
fun start() {
    backgroundThread.execute {
        // 相机初始化逻辑
        setCurrentState(CameraState.STARTED) // 子线程调用,触发异常
    }
}

// 相机组件内部正确写法
fun start() {
    backgroundThread.execute {
        // 相机初始化逻辑
        post { // 利用View的post方法切换到主线程
            setCurrentState(CameraState.STARTED)
        }
    }
}

如果是第三方库无法修改源码,保持解决方案1中的runOnUiThread包裹所有相机操作即可。

3. 检查Activity配置

确认Manifest中你的Activity没有配置不必要的configChanges,避免权限授权后触发Activity重建。

原理说明

  • Android所有UI状态更新必须在主线程执行,相机组件的状态变更属于UI操作范畴
  • 重复初始化相机组件会导致内部状态混乱,isCameraSetup标记位可以有效避免这个问题
  • 显式用runOnUiThread包裹操作,能规避第三方库或异步逻辑带来的线程切换风险

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 19:45:20