权限与位置: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 被切到后台,或者屏幕关了,还在获取位置。比如运动追踪、打车软件等待接单。

⚠️ 重要提醒: Android 11 之后,后台位置权限必须通过系统设置页面手动授予,不能直接在 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);
    }
}
💡 我的建议: 申请权限时,FINECOARSE 一起申请。用户如果拒绝精确位置,系统会自动降级到粗精度。这样用户体验更好,不会因为拒绝一次就完全用不了。

后台位置的特殊处理

Android 12 又加了一条新规:如果 App 在后台频繁获取位置,系统会弹出「位置访问提醒」,让用户选择「仅限使用期间」还是「始终允许」。如果用户选了「仅限使用期间」,你的后台位置代码就失效了。

怎么应对?我一般这么做:

  • 区分场景:只有真正需要后台位置的场景才申请,比如「到达提醒」「地理围栏」。
  • 降低频率:后台更新间隔至少 5 分钟以上,别每秒都去拿位置。
  • 使用 PendingIntent:通过 LocationManager.requestLocationUpdates() 配合 PendingIntent,让系统帮你管理,而不是自己开个 Service 死循环。

这里有个真实案例:我之前维护的一个外卖 App,骑手端需要后台持续上报位置。一开始我们每 10 秒上报一次,结果被系统疯狂杀进程,用户投诉「App 怎么老掉线」。后来改成每 2 分钟上报一次,配合 Foreground Service 显示通知,问题就解决了。

知识体系结构图

下面这张图把位置权限的完整决策流程画出来了,建议保存下来对照着写代码:

位置权限申请决策流程 开始申请位置权限 是否需要位置? 不申请,直接跳过 前台还是后台? 前台 申请 FINE + COARSE 引导用户去设置页面手动开启 注意:Android 11+ 后台位置必须通过系统设置授权

避坑指南

最后总结几个我踩过的坑,你们遇到了直接对照着排查:

  • 不要同时申请前台和后台位置:Android 11 之后,如果你在同一个 requestPermissions 里同时申请 FINEBACKGROUND,系统会忽略后台权限的申请。必须分两次。
  • 模拟器测试后台位置:真机上后台位置经常被系统限制,建议用 Android 模拟器 + adb shell dumpsys location 来调试,能看到系统到底有没有把位置传给 App。
  • 用户拒绝后怎么办:不要反复弹窗。我见过有些 App 用户拒绝一次就弹一次,结果被用户直接卸载。正确的做法是:引导用户去设置页面手动开启,并且只引导一次。
  • Android 12 的「大致位置」开关:用户可以在权限弹窗里选择「大致位置」而不是「精确位置」。如果你的 App 必须用精确位置,记得检查 isLocationEnabled() 和精度等级。

核心原则: 位置权限不是「越多越好」,而是「够用就好」。你申请的权限越少,用户越信任你,App 的留存率也越高。我见过太多 App 因为滥用位置权限被用户卸载,得不偿失。

好了,位置权限这块内容就这些。代码示例和流程图我都贴出来了,你们写的时候直接参考就行。记住一句话:尊重用户的隐私,用户才会尊重你的 App


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