权限与位置:ACCESS_FINE_LOCATION、ACCESS_COARSE_LOCATION、后台位置
说到位置权限,我估计很多开发者第一反应就是「直接申请 ACCESS_FINE_LOCATION 完事」。嗯,我以前也这么干过,直到被 Google Play 审核打回来三次……说白了,位置权限是 Android 运行时权限里最复杂的一块,没有之一。
为什么复杂?因为位置信息太敏感了。你想想看,一个 App 能知道你在哪、去过哪、什么时候去的,这要是被滥用,后果很严重。所以 Android 从 6.0 到 10.0 再到 12.0,几乎每个大版本都在收紧位置权限的控制。
三种位置精度,你分得清吗?
先看个表格,把三种位置权限的核心区别说清楚:
| 权限 | 精度 | 典型来源 | 功耗 |
|---|---|---|---|
ACCESS_FINE_LOCATION |
高(米级) | GPS + 网络 | 高 |
ACCESS_COARSE_LOCATION |
低(街区级) | WiFi / 基站 | 低 |
ACCESS_BACKGROUND_LOCATION |
取决于前台权限 | 同上 | 取决于实现 |
我个人习惯是:能用粗精度就别用细精度。比如天气 App,你只需要知道用户所在城市,ACCESS_COARSE_LOCATION 完全够用。非要申请 FINE,用户一看「一个天气软件要我的精确位置?」直接拒绝。
前台位置 vs 后台位置
这里有个坑,我踩过好几次。Android 10 引入了 ACCESS_BACKGROUND_LOCATION,但它的申请规则和前台位置完全不同。
前台位置:App 在前台运行时获取位置。比如地图导航,用户看着屏幕,这没问题。
后台位置:App 被切到后台,或者屏幕关了,还在获取位置。比如运动追踪、打车软件等待接单。
我曾经做过一个骑行轨迹记录 App,用户反馈「为什么我锁屏后轨迹就断了?」。查了半天,发现我只申请了前台位置权限,后台根本没处理。后来加上了 ACCESS_BACKGROUND_LOCATION,但用户又抱怨「为什么还要去设置里手动开?」——嗯,这就是 Android 的设计哲学:后台位置必须让用户明确知晓并主动授权。
代码实现:怎么申请才规范?
直接上代码,这是我项目里实际在用的模板:
// 检查前台位置权限
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// 需要解释为什么申请?
if (shouldShowRequestPermissionRationale(
Manifest.permission.ACCESS_FINE_LOCATION)) {
// 展示解释说明,再申请
showRationaleDialog()
}
// 申请前台位置权限
requestPermissions(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
),
LOCATION_PERMISSION_REQUEST_CODE
)
}
// 检查后台位置权限(Android 10+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_BACKGROUND_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// 引导用户去设置页面手动开启
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
}
}
FINE 和 COARSE 一起申请。用户如果拒绝精确位置,系统会自动降级到粗精度。这样用户体验更好,不会因为拒绝一次就完全用不了。
后台位置的特殊处理
Android 12 又加了一条新规:如果 App 在后台频繁获取位置,系统会弹出「位置访问提醒」,让用户选择「仅限使用期间」还是「始终允许」。如果用户选了「仅限使用期间」,你的后台位置代码就失效了。
怎么应对?我一般这么做:
- 区分场景:只有真正需要后台位置的场景才申请,比如「到达提醒」「地理围栏」。
- 降低频率:后台更新间隔至少 5 分钟以上,别每秒都去拿位置。
- 使用
PendingIntent:通过LocationManager.requestLocationUpdates()配合PendingIntent,让系统帮你管理,而不是自己开个 Service 死循环。
这里有个真实案例:我之前维护的一个外卖 App,骑手端需要后台持续上报位置。一开始我们每 10 秒上报一次,结果被系统疯狂杀进程,用户投诉「App 怎么老掉线」。后来改成每 2 分钟上报一次,配合 Foreground Service 显示通知,问题就解决了。
知识体系结构图
下面这张图把位置权限的完整决策流程画出来了,建议保存下来对照着写代码:
避坑指南
最后总结几个我踩过的坑,你们遇到了直接对照着排查:
- 不要同时申请前台和后台位置:Android 11 之后,如果你在同一个
requestPermissions里同时申请FINE和BACKGROUND,系统会忽略后台权限的申请。必须分两次。 - 模拟器测试后台位置:真机上后台位置经常被系统限制,建议用 Android 模拟器 +
adb shell dumpsys location来调试,能看到系统到底有没有把位置传给 App。 - 用户拒绝后怎么办:不要反复弹窗。我见过有些 App 用户拒绝一次就弹一次,结果被用户直接卸载。正确的做法是:引导用户去设置页面手动开启,并且只引导一次。
- Android 12 的「大致位置」开关:用户可以在权限弹窗里选择「大致位置」而不是「精确位置」。如果你的 App 必须用精确位置,记得检查
isLocationEnabled()和精度等级。
核心原则: 位置权限不是「越多越好」,而是「够用就好」。你申请的权限越少,用户越信任你,App 的留存率也越高。我见过太多 App 因为滥用位置权限被用户卸载,得不偿失。
好了,位置权限这块内容就这些。代码示例和流程图我都贴出来了,你们写的时候直接参考就行。记住一句话:尊重用户的隐私,用户才会尊重你的 App。