开启相机权限触发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
相关产品推荐
相关产品推荐

