14、权限与后台:后台位置权限、前台服务权限、Android 10+后台限制

聊到后台权限,我脑子里第一个蹦出来的画面,就是几年前那次线上事故。

用户反馈说:“你们的App怎么一退到后台就‘死’了?” 我排查了半天,发现是后台位置权限没处理好,系统直接把进程给杀了。嗯,从那以后,我对后台权限这块就格外上心。

说白了,Android从10开始,对后台的限制越来越严。你想想看,如果每个App都在后台偷偷定位、偷偷启动服务,用户的电量撑不过半天。所以Google下了狠手。

14.1 后台位置权限:ACCESS_BACKGROUND_LOCATION

这个权限,是Android 10(API 29)新增的。以前只要申请了前台位置权限,后台也能用。现在不行了。

你必须单独申请 ACCESS_BACKGROUND_LOCATION。而且,用户可以在设置里随时关掉它。

核心规则:

  • 如果你的App只在用户看得见的时候(前台)用位置,不需要后台权限。
  • 如果需要在后台持续获取位置(比如运动轨迹记录),必须申请后台权限。
  • 用户拒绝后台权限后,App不能反复弹窗骚扰用户。

我在项目中遇到过一种情况:用户明明给了前台位置权限,但App一退到后台,定位就停了。查了半天,发现是没加 ACCESS_BACKGROUND_LOCATION。这个坑,很多人踩过。

14.1.1 权限声明与申请

AndroidManifest.xml 里声明:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

注意顺序:先申请前台权限,再申请后台权限。Google建议分两步走。

代码申请时,我习惯这样写:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    // 先检查前台权限
    if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) 
            != PackageManager.PERMISSION_GRANTED) {
        requestPermissions(
            new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
            REQUEST_CODE_FOREGROUND
        );
    } else {
        // 前台权限已获取,再申请后台权限
        if (checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) 
                != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(
                new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
                REQUEST_CODE_BACKGROUND
            );
        }
    }
}

小技巧: 申请后台权限时,最好给用户一个明确的理由。比如弹个对话框说“我们需要后台定位来记录你的运动轨迹”,而不是直接弹系统权限框。用户更容易接受。

14.2 前台服务权限:FOREGROUND_SERVICE

Android 9(API 28)引入了前台服务权限。说白了,就是告诉系统:“我这个服务很重要,你别杀我。”

前台服务会显示一个持续的通知(Notification),用户能看到你在后台干活。

14.2.1 声明与使用

AndroidManifest.xml 里声明:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

启动前台服务的代码:

Intent serviceIntent = new Intent(this, MyForegroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(serviceIntent);
} else {
    startService(serviceIntent);
}

MyForegroundServiceonStartCommand 里,必须调用 startForeground()

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Notification notification = buildNotification();
    startForeground(NOTIFICATION_ID, notification);
    // 执行后台任务
    return START_STICKY;
}

警告: 如果不调用 startForeground(),系统会在5秒内抛出 ForegroundServiceDidNotStartInTimeException,直接崩溃。我曾经因为这个被测试妹子追着骂了一下午。

14.3 Android 10+ 后台限制:后台启动Activity与Service

Android 10开始,系统对后台启动Activity做了严格限制。说白了,你的App在后台时,不能随便弹出一个Activity来打扰用户。

我记得有一次,用户反馈说“手机突然自己弹出个界面”,查了日志发现是某个第三方SDK在后台启动了Activity。嗯,这种体验非常糟糕。

14.3.1 后台启动Activity的限制

从Android 10开始,以下情况不允许后台启动Activity:

  • App处于后台状态(没有可见的Activity)
  • 用户没有与App交互
  • 不是从通知、Shortcut、Widget等系统入口启动

如果你非要后台启动,系统会弹一个Toast提示:“App已阻止后台启动Activity”。

解决方案:

  • 使用通知(Notification)来提醒用户,点击通知再启动Activity。
  • 使用前台服务,保持App在前台状态。
  • 使用 PendingIntent 配合通知或Shortcut。

14.3.2 后台启动Service的限制

Android 8.0(API 26)开始,后台启动Service就被限制了。Android 10进一步收紧。

核心规则:

  • App处于后台时,不能调用 startService()
  • 可以使用 startForegroundService(),但必须在5秒内调用 startForeground()
  • 可以使用 JobSchedulerWorkManager 来替代后台Service。

我个人习惯,能用 WorkManager 绝不用原生Service。WorkManager会自动处理兼容性问题,省心很多。

14.4 知识体系图:后台权限与限制

下面这张图,是我自己总结的。每次做新项目,我都会拿出来对照一遍。

Android 10+ 后台权限与限制体系 后台权限与限制 后台位置权限 ACCESS_BACKGROUND_LOCATION 需单独申请 用户可随时关闭 前台服务权限 FOREGROUND_SERVICE 必须显示通知 5秒内调用startForeground() 后台启动限制 禁止后台启动Activity 禁止后台startService() 推荐使用WorkManager 核心原则:用户可见、用户可控、最小权限 后台权限必须给用户明确的理由,不能偷偷摸摸

14.5 避坑指南:我踩过的那些坑

做Android这么多年,后台权限这块的坑,我基本都踩了一遍。分享几个典型的:

坑1:后台位置权限申请时机不对

我曾经在App启动时同时申请前台和后台位置权限。结果用户直接拒绝,而且勾选了“不再询问”。后来我改成先申请前台权限,等用户用到轨迹记录功能时,再弹窗解释为什么需要后台权限。通过率提高了不少。

坑2:前台服务忘记调startForeground()

这个我前面提过。有一次上线前测试,发现App启动后5秒必崩。查了半天,发现是某个分支逻辑里忘了调 startForeground()。嗯,从那以后我写了个单元测试来检查这个。

坑3:后台启动Activity被系统拦截

有个需求是:收到推送后直接跳转到某个页面。结果在Android 10上,用户点击推送后没反应,只弹了个Toast。后来改成点击推送先打开MainActivity,再通过Intent跳转。完美解决。

14.6 总结

后台权限这块,说白了就是三个字:别作死

系统限制越来越严,是为了保护用户隐私和体验。我们作为开发者,要做的不是绕开限制,而是顺应规则,用更优雅的方式实现功能。

我个人建议:

  • 能用前台服务,就别用后台Service。
  • 能用WorkManager,就别自己管理线程。
  • 申请权限时,一定要给用户一个合理的解释。
  • 测试时,务必在Android 10+的真机上跑一遍后台场景。

嗯,今天就聊到这里。记住,权限不是你想拿,想拿就能拿。


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