16、Android 10+ 隐私变更:后台传感器访问限制、前台服务声明、用户权限控制

距离传感器这东西,在 Android 10 之前,说实话挺“野”的。你想读就读,想后台读就后台读,系统基本不管。但从 Android 10(API 29)开始,Google 终于动手了。我当年第一次适配这个变更时,差点被线上崩溃搞到怀疑人生——嗯,今天我们就来聊聊这些“坑”到底在哪。

16.1 后台传感器访问限制:系统到底在防什么?

说白了,Android 10 引入了一个核心原则:后台应用不能随意访问连续传感器。这里的“连续传感器”包括距离传感器、加速度计、陀螺仪等。为什么?因为有些流氓应用会在后台偷偷用传感器来“监听”用户行为——比如通过加速度计推断你是在走路还是坐着,甚至推测你的位置。

我个人习惯把传感器访问分为两类:

  • 前台访问:应用可见时,随便读,没问题。
  • 后台访问:应用不可见时,系统直接屏蔽传感器事件。

你想想看,如果你的 App 在后台还想读距离传感器,比如做防误触功能,那系统会直接让你的 SensorEventListener 收不到任何回调。这不是 bug,是设计。

核心规则:从 Android 10 开始,后台应用无法注册连续传感器事件的监听器。如果强行注册,系统会静默忽略,不会抛异常,但你也拿不到数据。

16.2 前台服务声明:唯一的“后门”

那如果我真的需要在后台用距离传感器呢?比如一个通话防误触 App,用户锁屏后还得工作。这时候,前台服务就是唯一的合法路径。

我在项目中遇到过这样一个场景:一个老人机模式的 App,需要在后台持续检测距离传感器来判断手机是否贴近耳朵。一开始我直接注册了监听器,结果 Android 10 设备上完全没反应。查了半天才发现——必须用前台服务。

具体怎么做?三步走:

  1. AndroidManifest.xml 中声明前台服务类型。
  2. 启动前台服务时,传入 FOREGROUND_SERVICE_TYPE_SENSORS
  3. 在服务中注册传感器监听器。

代码示例:

<!-- AndroidManifest.xml -->
<service
    android:name=".SensorForegroundService"
    android:foregroundServiceType="sensors"
    android:exported="false" />
// 启动前台服务
Intent serviceIntent = new Intent(this, SensorForegroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    startForegroundService(serviceIntent);
} else {
    startService(serviceIntent);
}

// 在 Service 的 onCreate 中
@Override
public void onCreate() {
    super.onCreate();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        startForeground(
            NOTIFICATION_ID,
            buildNotification(),
            FOREGROUND_SERVICE_TYPE_SENSORS
        );
    }
    // 注册传感器监听器
    SensorManager sensorManager = getSystemService(SensorManager.class);
    Sensor proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
    sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
}

注意:前台服务必须显示一个持续的通知。用户是可以手动关闭通知的,一旦通知被移除,系统会杀掉你的前台服务。我曾经因为这个被用户投诉“为什么 App 突然不工作了”——所以记得在通知里写清楚用途。

16.3 用户权限控制:距离传感器也要权限?

等等,距离传感器不是不需要权限吗?嗯,从 Android 10 开始,普通传感器仍然不需要运行时权限。但如果你用了前台服务,用户是可以看到“XX应用正在使用传感器”的提示的。这其实是一种隐性的权限控制——用户如果觉得不对劲,可以直接杀掉你的前台服务。

另外,从 Android 12 开始,Google 引入了 BLUETOOTH_CONNECTBODY_SENSORS 等权限的细化,但距离传感器依然不在运行时权限列表里。不过,我建议你在应用内做一个开关,让用户主动选择是否开启“防误触”或“通话感应”功能。为什么?因为有些用户就是不喜欢 App 在后台偷偷用传感器,哪怕你是为了他好。

Android 版本 后台传感器访问 前台服务要求 用户权限控制
Android 9 (API 28) 及以下 无限制 不需要
Android 10 (API 29) 后台禁止连续传感器 需要声明 foregroundServiceType="sensors" 用户可关闭前台服务通知
Android 12 (API 31) 同上,更严格 同上,且通知不可关闭(部分厂商) 系统提示“传感器使用中”

16.4 避坑指南:我踩过的三个雷

讲真,这部分内容我本来想一笔带过,但想想还是写出来吧——毕竟都是真金白银换来的教训。

  • 我曾经在 Android 10 上直接注册传感器监听器,没加前台服务。结果在后台时监听器完全没回调,但 Logcat 里没有任何错误。排查了整整两天才发现是系统行为。所以,记得在 onSensorChanged 里加一个日志,如果长时间没回调,先检查应用是否在前台。
  • 我曾经忘了在 Manifest 里声明 foregroundServiceType。启动前台服务时直接崩溃,报错信息是 Foreground service type not set。这个错误很明确,但如果你用的是第三方库,可能被吞掉。建议在 startForeground 前后加 try-catch。
  • 我曾经在低版本设备上用了高版本 API。比如在 Android 9 上调用 startForeground 传了 FOREGROUND_SERVICE_TYPE_SENSORS,直接编译不过。记得加版本判断:if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)

小技巧:如果你只是想在应用可见时用距离传感器,完全不需要前台服务。在 onPause 里注销监听器,在 onResume 里重新注册就行。这样既省电,又不用处理复杂的后台逻辑。

16.5 知识体系图:Android 10+ 传感器访问决策流程

下面这张图是我自己整理的,每次做新项目都会看一眼。你想想看,有了这个图,适配起来是不是清晰多了?

Android 10+ 传感器访问决策流程 应用需要传感器数据 应用在前台? 直接注册监听器 需要前台服务 声明了 sensors 类型? 启动前台服务 崩溃 总结:前台直接读,后台必须前台服务 + 声明 sensors 类型

16.6 写在最后

Android 10+ 的隐私变更,说白了就是一句话:别在后台偷偷用传感器。如果你有正当需求,就用前台服务,并且让用户知道你在做什么。我个人觉得这个设计其实挺合理的——既保护了用户隐私,又给正经应用留了活路。

嗯,适配的时候记得多测几种机型,尤其是国产厂商的 ROM,它们有时候会对前台服务做额外的限制。我曾经在某个品牌手机上遇到过,前台服务通知被系统默认隐藏了,结果传感器直接罢工。最后只能加了一个引导用户打开通知权限的弹窗。

好了,这一章就到这里。记住:传感器权限不是“没有权限”,而是“隐式权限”。你尊重用户,系统才会尊重你的 App。


公众号:蓝海资料掘金营,微信deep3321