PAYATHON 2026

Android 16 后台调用 getCameraCharacteristics() 为何崩溃?

数据小陈

结论

从这段堆栈看,异常出现在 CameraManager.getCameraCharacteristics() 内部调用 getRotationOverride(),并通过 ActivityManager.AppTask.getTaskInfo() 查询任务信息时。系统报告未找到对应的 task ID。

现有信息无法确认这是 Android 16 的系统缺陷,也无法确定具体触发条件。应用处于后台、使用 foreground service 时出现异常,只能说明当时的场景,不能据此认定后台状态是原因。可以先在相机特征读取路径上处理这类失败,避免异常导致进程崩溃,同时补充日志继续排查。

先从堆栈定位

关键调用链是:

CameraManager.getCameraCharacteristics()
  -> CameraManager.getRotationOverride()
  -> ActivityManager.AppTask.getTaskInfo()
  -> IllegalArgumentException: Unable to find task ID <编号>

堆栈显示,异常不是由应用代码直接抛出,而是在 framework 调用过程中出现。调用返回前,系统尝试根据任务信息计算 rotation override,但查询时没有找到对应的 ID。仅凭这段堆栈,无法判断任务何时失效,也无法确定这一行为是否符合某项 API 保证。

建议的处理方式

1. 在相机初始化边界处理异常

把异常处理放在读取相机特征的调用附近。遇到这类失败时,记录相机 ID、线程、应用前后台状态和设备系统版本,然后跳过本次初始化或延后重试。不要用伪造的 CameraCharacteristics 继续配置相机。

fun getCameraCharacteristicsSafely(
    cameraManager: CameraManager,
    cameraId: String
): CameraCharacteristics? {
    return try {
        cameraManager.getCameraCharacteristics(cameraId)
    } catch (e: IllegalArgumentException) {
        if (e.message?.contains("Unable to find task ID") == true) {
            // 记录 cameraId、设备型号、系统版本及应用状态。
            // 由调用方延后处理或跳过本次相机初始化。
            null
        } else {
            throw e
        }
    }
}

调用方需要检查返回值。若为 null,可以等应用界面恢复稳定后进行一次有上限的重试,也可以告知用户本次无法启动相机。避免无延迟、无限次重试,也不要把其他 IllegalArgumentException 当作可忽略错误。

2. 检查调用时序和任务生命周期

为每次读取记录时间戳、相机 ID、调用线程、前后台状态,以及相关 Activity 是否正在结束或重建。重点排查调用是否与任务退出、Activity 销毁或应用切换状态同时发生。现有材料没有这些生命周期信息,因此这些只是排查方向,不能视为已确认的根因。

3. 利用 Crashlytics 缩小范围

按设备型号、Android 16 的具体系统构建版本、应用版本和调用场景分组查看事件。现有报告显示多个厂商的设备都出现了问题,可以先比较它们共同的系统版本和应用调用路径。仅凭这些统计,还不能认定所有 Android 16 设备都有相同缺陷。

如果可以增加非敏感的诊断信息,也记录 cameraId、调用时是否已有相机正在打开,以及本次调用最终是成功、延后还是放弃。复现时补充最小调用代码和完整的设备系统构建信息,以便判断问题是否与特定系统实现有关。

注意事项

异常中的 task ID 是系统任务查询使用的标识,应用侧不应自行猜测或替换。当前堆栈只能说明 framework 查询该任务时失败,无法证明 getCameraCharacteristics() 在所有场景下都不安全,也无法确认某种重试或生命周期调整一定能根治问题。先在局部做好兜底并收集调用时序信息,再根据更多样本判断是否需要调整相机初始化流程。

备注:内容仅供参考。