Android TileService(快捷设置)导致Activity关闭时触发Camera ERROR_CAMERA_DISABLED错误的问题咨询
Android TileService(快捷设置)导致Activity关闭时触发Camera ERROR_CAMERA_DISABLED错误的问题咨询
大家好,我最近在Android开发中碰到了一个特别诡异的问题,想来请教下各位大佬:
当我的应用里同时存在一个带相机预览的Activity,以及一个快捷设置Tile(这个Tile的逻辑和相机完全无关,甚至可以是空实现,我下面会附示例代码)时,会出现这样的异常场景:
如果TileService还处于存活状态(也就是它的onDestroy方法还没被系统调用),当我通过返回键或Home键关闭/隐藏Activity(此时Activity的onStop方法被触发),CameraDevice.StateCallback就会触发onError回调,错误码是ERROR_CAMERA_DISABLED(对应值为3)。
我完全搞不懂这两者之间为什么会有牵连,也不确定Google Issue Tracker上有没有人上报过这个问题——我用关键词搜了一圈,没找到相关的记录,所以来这里求助了。
问题复现的日志记录
01:50:45.787 [Test]TileService D onCreate 01:50:45.799 [Test]TileService D onStartListening 01:50:49.044 [Test]TileService D onStopListening 01:50:53.270 [Test]Activity D onCreate 01:50:53.289 [Test]Activity D onStart 01:50:53.356 [Test]Activity D Camera onOpened 01:50:56.965 [Test]Activity D onStop 01:50:58.745 [Test]Activity D onStart 01:50:58.764 [Test]Activity D Camera onOpened 01:51:00.724 [Test]Activity D onStop 01:51:00.937 [Test]Activity E Camera onError: 3 01:51:19.056 [Test]TileService D onDestroy
从日志里能看到关键的触发条件:
- 只有当TileService存活时(
onDestroy未调用),关闭Activity才会触发相机的onError:3 - 一旦TileService被系统销毁(日志最后一行的
onDestroy),后续再操作Activity就不会出现这个错误了
另外,激活TileService的方式很简单,只要下拉打开系统通知面板,就能让它进入存活状态,之后过一段时间系统才会自动销毁它。
复现用的示例代码
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.CAMERA" /> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.CameraDisabledWithTileServiceAlive" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true" android:theme="@style/Theme.CameraDisabledWithTileServiceAlive"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".MyTileService" android:exported="true" android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> <intent-filter> <action android:name="android.service.quicksettings.action.QS_TILE" /> </intent-filter> </service> </application> </manifest>
编译配置说明
compileSdk/targetSdk 为35或36,包名为com.cameradisabled.tileservice
MainActivity.kt(核心相机逻辑代码)
import android.Manifest import android.content.Context import android.content.pm.PackageManager import android.graphics.SurfaceTexture import android.hardware.camera2.CameraCaptureSession import android.hardware.camera2.CameraDevice import android.hardware.camera2.CameraManager import android.hardware.camera2.CaptureRequest import android.os.Bundle import android.util.Log import android.view.Surface import android.view.TextureView import android.view.View import android.widget.Button import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat class MainActivity : AppCompatActivity() { companion object { private const val TAG = "[Test]Activity" } private lateinit var textureView: TextureView private lateinit var btnRequest: Button private lateinit var cameraManager: CameraManager private val permissionLauncher = registerForActivityResult( ActivityResultContracts.RequestPermission() ) { isGranted -> Log.d(TAG, "Permission result: $isGranted") if (isGranted) { btnRequest.visibility = View.GONE openCameraIfReady() } else { btnRequest.visibility = View.VISIBLE } } private var cameraDevice: CameraDevice? = null private var session: CameraCaptureSession? = null private lateinit var previewRequestBuilder: CaptureRequest.Builder override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d(TAG, "onCreate") setContentView(R.layout.activity_main) textureView = findViewById(R.id.texture_view) btnRequest = findViewById(R.id.btn_request_permission) cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager } override fun onStart() { super.onStart() Log.d(TAG, "onStart") if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { openCameraIfReady() } } override fun onStop() { super.onStop() Log.d(TAG, "onStop") // 标准的相机资源释放逻辑 session?.close() cameraDevice?.close() } private fun openCameraIfReady() { if (textureView.isAvailable) { // 打开相机的逻辑,通过CameraManager获取相机ID并打开 } else { textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener { override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) { openCameraIfReady() } override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {} override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean = true override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {} } } } }
MyTileService.kt(空实现的快捷设置Tile)
import android.service.quicksettings.TileService import android.util.Log class MyTileService : TileService() { companion object { private const val TAG = "[Test]TileService" } override fun onCreate() { super.onCreate() Log.d(TAG, "onCreate") } override fun onStartListening() { super.onStartListening() Log.d(TAG, "onStartListening") } override fun onStopListening() { super.onStopListening() Log.d(TAG, "onStopListening") } override fun onDestroy() { super.onDestroy() Log.d(TAG, "onDestroy") } }
我已经反复测试过这个场景,只要满足TileService存活+Activity关闭的条件,就大概率会触发这个相机错误,有没有大佬遇到过类似的问题,或者能解释下这背后的原因?
内容来源于stack exchange
相关产品推荐
相关产品推荐

