应用在后台运行时,会消耗设备上有限的资源(如 RAM)。这可能会导致用户体验受损,特别是当用户正在使用资源密集型应用(例如玩游戏或观看视频)时。为了改善用户体验,Android 8.0(API 级别 26)对应用在后台运行时可以执行的操作施加了限制。本文档描述了操作系统的变化,以及您如何更新您的应用以在新限制下良好运行。
概览
许多 Android 应用和服务可以同时运行。例如,用户可以在一个窗口中玩游戏,同时在另一个窗口中浏览网页,并使用第三个应用播放音乐。同时运行的应用越多,系统承受的负载就越大。如果额外的应用或服务在后台运行,这会给系统带来额外的负载,可能导致糟糕的用户体验;例如,音乐应用可能会突然关闭。
为了降低这些问题的发生几率,Android 8.0 对应用在用户未直接与它们交互时可以执行的操作施加了限制。应用受到两方面的限制:
后台服务限制:当应用空闲时,其后台服务的使用受到限制。这不适用于前台服务,前台服务对用户来说更显眼。
广播限制:除少数例外情况,应用不能使用其清单注册隐式广播。它们仍然可以在运行时注册这些广播,并且可以使用清单注册显式广播以及专门针对其应用的广播。
在大多数情况下,应用可以通过使用 JobScheduler
作业来规避这些限制。这种方法允许应用在不主动运行时安排工作,但仍为系统提供了安排这些作业的余地,使其不会影响用户体验。Android 8.0 对 JobScheduler
进行了多项改进,使其更易于用计划作业替换服务和广播接收器;有关更多信息,请参阅 JobScheduler 改进。
后台服务限制
在后台运行的服务可能会消耗设备资源,从而可能导致更差的用户体验。为了缓解此问题,系统对服务施加了多项限制。
系统区分前台应用和后台应用。(服务限制中的后台定义与内存管理所使用的定义不同;一个应用可能在内存管理方面处于后台,但在启动服务方面处于前台。)如果满足以下任一条件,则应用被视为在前台:
- 它具有可见的 activity,无论该 activity 是已启动还是已暂停。
- 它具有前台服务。
- 另一个前台应用连接到该应用,通过绑定到其服务之一或利用其内容提供程序。例如,如果另一个应用绑定到其以下服务,则该应用位于前台:
- IME
- 壁纸服务
- 通知监听器
- 语音或文本服务
如果以上条件均不满足,则该应用被视为在后台。
当应用在前台时,它可以自由创建和运行前台和后台服务。当应用进入后台时,它有几分钟的时间窗口,在此期间仍然允许创建和使用服务。在此窗口结束时,该应用被视为处于空闲状态。此时,系统会停止应用的后台服务,就像应用调用了服务的 Service.stopSelf()
方法一样。
在某些情况下,后台应用会被临时加入允许列表,持续几分钟。当应用在允许列表上时,它可以无限制地启动服务,并且其后台服务被允许运行。当应用处理用户可见的任务时,例如:
- 处理高优先级 Firebase Cloud Messaging (FCM) 消息。
- 接收广播,例如短信/彩信消息。
- 从通知执行
PendingIntent
。 - 在 VPN 应用提升到前台之前启动
VpnService
。
在许多情况下,您的应用可以用 JobScheduler
作业替换后台服务。例如,CoolPhotoApp 需要检查用户是否收到了朋友分享的照片,即使该应用未在前台运行。以前,该应用使用了一个后台服务来检查应用的云存储。为了迁移到 Android 8.0(API 级别 26),开发者将后台服务替换为计划作业,该作业会定期启动,查询服务器,然后退出。
在 Android 8.0 之前,创建前台服务的通常方法是创建后台服务,然后将该服务提升到前台。对于 Android 8.0,有一个复杂之处;系统不允许后台应用创建后台服务。因此,Android 8.0 引入了新方法 startForegroundService()
,用于在前台启动新服务。系统创建服务后,应用有五秒钟时间调用服务的 startForeground()
方法以显示新服务的用户可见通知。如果应用在规定时间内未调用 startForeground()
,系统将停止该服务并声明该应用处于 ANR 状态。
广播限制
如果应用注册接收广播,则每次发送广播时,应用的接收器都会消耗资源。如果太多应用注册以接收基于系统事件的广播,这可能会导致问题;触发广播的系统事件可能导致所有这些应用快速连续消耗资源,从而损害用户体验。为了缓解此问题,Android 7.0(API 级别 24)对广播施加了限制,如后台优化中所述。Android 8.0(API 级别 26)使这些限制更加严格。
- 以 Android 8.0 或更高版本为目标的应用不再能在其清单中注册隐式广播的广播接收器,除非该广播专门限制于该应用。隐式广播是不针对应用内特定组件的广播。例如,
ACTION_PACKAGE_REPLACED
会发送给所有应用中所有已注册的监听器,告知它们设备上的某个软件包已被替换。由于该广播是隐式的,它将不会传递给以 Android 8.0 或更高版本为目标的应用中通过清单注册的接收器。ACTION_MY_PACKAGE_REPLACED
也是一个隐式广播,但由于它仅发送给其软件包被替换的应用,因此它将传递给通过清单注册的接收器。 - 应用可以继续在其清单中注册显式广播。
- 应用可以在运行时使用
Context.registerReceiver()
注册任何广播的接收器,无论是隐式还是显式。 - 需要签名权限的广播不受此限制,因为这些广播仅发送给使用相同证书签名的应用,而不是设备上的所有应用。
在许多情况下,以前注册隐式广播的应用可以通过使用 JobScheduler
作业获得类似的功能。例如,一个社交照片应用可能需要不时对其数据进行清理,并且更喜欢在设备连接到充电器时执行此操作。以前,该应用在其清单中注册了 ACTION_POWER_CONNECTED
的接收器;当应用收到该广播时,它会检查是否需要清理。为了迁移到 Android 8.0 或更高版本,该应用从其清单中删除了该接收器。取而代之的是,该应用安排了一个清理作业,在设备空闲和充电时运行。
迁移指南
默认情况下,这些更改仅影响以 Android 8.0(API 级别 26)或更高版本为目标的应用。但是,用户可以从设置屏幕为任何应用启用这些限制,即使该应用的目标 API 级别低于 26。您可能需要更新您的应用以符合新的限制。
检查您的应用如何使用服务。如果您的应用依赖在应用空闲时在后台运行的服务,您将需要替换它们。可能的解决方案包括:
- 如果您的应用需要在后台时创建前台服务,请使用
startForegroundService()
方法而不是startService()
。 - 如果服务对用户可见,请将其设为前台服务。例如,播放音频的服务应始终是前台服务。使用
startForegroundService()
方法而不是startService()
来创建服务。 - 寻找一种方法,用计划作业来复制服务的功能。如果服务没有立即引起用户注意,您通常可以使用计划作业来代替。
- 当网络事件发生时,使用 FCM 选择性地唤醒您的应用程序,而不是在后台轮询。
- 将后台工作推迟到应用程序自然处于前台时进行。
检查您的应用清单中定义的广播接收器。如果您的清单声明了受影响的隐式广播的接收器,则必须替换它。可能的解决方案包括:
- 通过调用
Context.registerReceiver()
在运行时创建接收器,而不是在清单中声明接收器。 - 使用计划作业检查会触发隐式广播的条件。