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

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

相关产品推荐
方舟 Agent Plan

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

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