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 设备上完全没反应。查了半天才发现——必须用前台服务。
具体怎么做?三步走:
- 在
AndroidManifest.xml中声明前台服务类型。 - 启动前台服务时,传入
FOREGROUND_SERVICE_TYPE_SENSORS。 - 在服务中注册传感器监听器。
代码示例:
<!-- 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_CONNECT、BODY_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+ 传感器访问决策流程
下面这张图是我自己整理的,每次做新项目都会看一眼。你想想看,有了这个图,适配起来是不是清晰多了?
16.6 写在最后
Android 10+ 的隐私变更,说白了就是一句话:别在后台偷偷用传感器。如果你有正当需求,就用前台服务,并且让用户知道你在做什么。我个人觉得这个设计其实挺合理的——既保护了用户隐私,又给正经应用留了活路。
嗯,适配的时候记得多测几种机型,尤其是国产厂商的 ROM,它们有时候会对前台服务做额外的限制。我曾经在某个品牌手机上遇到过,前台服务通知被系统默认隐藏了,结果传感器直接罢工。最后只能加了一个引导用户打开通知权限的弹窗。
好了,这一章就到这里。记住:传感器权限不是“没有权限”,而是“隐式权限”。你尊重用户,系统才会尊重你的 App。
公众号:蓝海资料掘金营,微信deep3321