AMS 在 Android 12/13/14 中的变化:前台服务限制、广播限制、后台启动限制

聊到 Android 12 到 14 的 AMS 变化,我脑子里第一个蹦出来的词就是「收紧」。

说白了,Google 这几年一直在跟开发者「抢」后台资源。你想想看,用户手机里的 App 越来越多,如果每个都能随意在后台搞事情,那续航和流畅度就全完了。所以从 Android 12 开始,AMS 对前台服务、广播和后台启动的限制,是一年比一年狠。

一、前台服务限制:从「告知」到「强制」

前台服务,原本是给用户一个可见的通知,告诉用户「我在后台干活」。但很多 App 滥用这个机制,挂个通知就赖在后台不走。

Android 12 的变化:

  • 新增了 foregroundServiceType 属性。你必须在 Manifest 里声明你的前台服务是干嘛的(比如 cameralocationdataSync)。
  • 如果类型不匹配,系统会直接抛出异常。我记得第一次适配时,有个同事没加类型声明,结果一启动就崩溃,排查了半天。

核心变化:Android 12 要求前台服务必须「名正言顺」。你不能随便起一个前台服务,必须告诉系统你要用哪个能力。

Android 13 的加码:

  • 引入了「前台服务启动限制」:如果 App 在后台运行,不能随意启动前台服务。除非用户主动授予了 POST_NOTIFICATIONS 权限。
  • 说白了,用户不点头,你连通知都发不了,更别提前台服务了。

我的建议:如果你的 App 需要长时间后台运行,尽早适配 foregroundServiceType,并且引导用户授予通知权限。否则在 Android 13 上,你的前台服务可能根本起不来。

Android 14 的最终形态:

  • 进一步收紧了后台启动前台服务的条件。只有特定场景(如闹钟、通知点击、地理位置变化)才允许。
  • AMS 会检查调用栈,如果发现是后台广播或 JobScheduler 触发的,直接拒绝。

避坑指南:我曾经遇到一个项目,用 JobScheduler 定时启动前台服务做数据同步。在 Android 14 上直接失效了。解决方案是改用 WorkManagerForegroundService 模式,或者使用 AlarmManager 的精确闹钟。

二、广播限制:隐式广播的「大清洗」

广播限制其实从 Android 8 就开始了,但 Android 12-14 又补了几刀。

Android 12 的调整:

  • 所有隐式广播(不指定包名的广播)都不能在 Manifest 里静态注册了。除非是系统广播(如 BOOT_COMPLETED)。
  • AMS 在注册广播时,会检查 Intent 的 package 字段。如果为空,直接忽略。

为什么会这样?因为隐式广播太容易被第三方 App 拦截或伪造了。你想想看,一个广播发出去,所有 App 都能收到,这安全风险多大。

Android 13 的补充:

  • 新增了 RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED 标志。动态注册广播时,必须明确指定是否导出。
  • 如果不指定,AMS 会抛异常。嗯,这里要注意,很多老代码直接 registerReceiver(receiver, filter),在 Android 13 上会崩溃。

适配要点:动态注册广播时,一定要加上 RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED。如果只在本 App 内使用,用 RECEIVER_NOT_EXPORTED 更安全。

Android 14 的最终限制:

  • 进一步限制了「上下文注册的广播」:如果 App 在后台,不能通过 Context.registerReceiver() 注册广播。除非是系统广播或使用了 FLAG_RECEIVER_EXPORTED
  • AMS 会检查调用者的进程状态,如果是 PROCESS_STATE_IMPORTANT_BACKGROUND 或更低,直接拒绝注册。
版本 广播限制要点 影响范围
Android 12 隐式广播禁止静态注册 所有第三方 App
Android 13 动态注册必须指定导出标志 所有动态注册的广播
Android 14 后台不能动态注册广播 后台进程

三、后台启动限制:从「允许」到「几乎禁止」

后台启动 Activity,这个限制是最让开发者头疼的。因为很多场景(比如点击通知、拉起支付页面)都需要从后台启动 Activity。

Android 12 的严格模式:

  • 引入了「后台启动 Activity 限制」:默认情况下,后台 App 不能启动 Activity。除非有用户交互的上下文(如通知点击、PendingIntent 来自前台)。
  • AMS 会检查调用者的 uidpid,如果发现是后台进程,直接拦截并打印日志。

我记得当时有个项目,用户点击通知后拉起一个 Activity,结果在 Android 12 上死活没反应。查了半天才发现,通知的 PendingIntent 没有设置 FLAG_IMMUTABLEFLAG_UPDATE_CURRENT,导致 AMS 认为这是不安全的启动。

解决方案:所有通知的 PendingIntent 必须使用 FLAG_IMMUTABLE(Android 12 强制要求)。同时,确保 PendingIntent 是在前台创建的,或者通过 startActivity()ActivityOptions 传递 makeBasic() 标志。

Android 13 的细化:

  • 增加了「后台启动 Activity 的白名单」:系统会允许一些特殊场景(如 SYSTEM_ALERT_WINDOW 权限、设备管理员等)。
  • AMS 的检查逻辑更精细了:不仅看进程状态,还会看调用栈的调用链。如果是从 BroadcastReceiveronReceive() 中启动,且广播是系统广播,允许启动。

Android 14 的最终形态:

  • 几乎完全禁止后台启动 Activity。只有极少数场景(如闹钟、通知点击、辅助功能服务)才允许。
  • AMS 新增了 STOP_ACTIVITY_ON_BACKGROUND 标志,如果检测到后台启动,直接返回 false,并记录 ActivityManager.APP_START_BG_ACTIVITY 事件。

避坑指南:我曾经在 Android 14 上遇到一个 case:App 在后台收到 FCM 推送,然后尝试拉起一个 Activity 展示详情。结果 AMS 直接拒绝了。最终方案是改用 Notification + PendingIntent 的方式,让用户点击通知后再启动 Activity。这样既符合规范,又不会触发限制。

四、核心逻辑流程图

下面这张图,是我根据 Android 14 的 AMS 源码梳理的。它展示了 AMS 在收到启动请求时,如何一步步判断是否允许。

AMS 后台启动限制判断流程(Android 14) 收到启动请求 进程是否在前台? 允许启动 是否特殊场景? 允许启动 是否有合法PendingIntent? 允许启动 拒绝启动 特殊场景:闹钟、通知点击、辅助功能、SYSTEM_ALERT_WINDOW 权限等

五、总结与适配建议

从 Android 12 到 14,AMS 的变化可以用一句话概括:后台权限越来越小,前台行为越来越规范

我个人习惯在适配新版本时,先跑一遍 CTS 的 BackgroundActivityLaunchTestForegroundServiceTest,看看自己的 App 会不会触发限制。如果发现异常,优先考虑用 WorkManagerNotification 替代直接的后台启动。

嗯,最后提醒一句:不要试图绕过这些限制。AMS 的检查逻辑在内核层,hook 或反射的方式在 Android 14 上基本都失效了。老老实实适配,才是正道。