后台执行限制

每当应用在后台运行时,都会消耗设备的有限资源(例如 RAM)。这可能会影响用户体验,当用户使用占用大量资源的应用(如玩游戏或观看视频)时尤其如此。为了提升用户体验,Android 8.0(API 级别 26)对应用在后台运行时可以执行的操作施加了限制。本文档介绍了对操作系统所做的更改,以及如何更新应用以便在新限制下正常运行。

概览

许多 Android 应用和服务可以同时运行。例如,用户可能在一个窗口中玩游戏,同时在另一个窗口中浏览网页,并使用第三个应用播放音乐。同时运行的应用越多,对系统造成的负担就越大。如果其他应用或服务在后台运行,这会对系统造成额外的负载,进而可能导致用户体验不佳;例如,音乐应用可能会突然关闭。

为了降低出现这些问题的几率,Android 8.0 对应用在用户不直接与应用互动时可以执行的操作施加了限制。应用在两个方面受到限制:

  • 后台服务限制:应用处于空闲状态时,其后台服务的使用会受到限制。这不适用于前台服务,因为前台服务更容易被用户注意到。

  • 广播限制:除了少数例外情况,应用无法使用其清单注册隐式广播。它们仍然可以在运行时注册这些广播,并且可以使用清单注册显式广播和专门针对其应用的广播。

在大多数情况下,应用可以使用 JobScheduler 作业克服这些限制。这种方法可让应用安排在其未活跃运行时执行工作,但系统仍可在不影响用户体验的情况下调度这些作业。Android 8.0 对 JobScheduler 进行了多项改进,可让您更轻松地用计划作业取代服务和广播接收器;如需了解详情,请参阅 JobScheduler 改进

后台服务限制

在后台运行的服务会消耗设备资源,这可能会降低用户体验。为了缓解这一问题,系统对服务施加了一些限制。

系统可以区分前台应用和后台应用。 (出于服务限制目的的后台定义与内存管理所使用的定义不同;根据内存管理的规定,应用可能处于后台,但根据其启动服务的能力,处于前台。)应用满足以下任一条件即视为前台应用:

  • 它具有可见的 Activity,无论 Activity 处于启动还是暂停状态。
  • 它具有前台服务。
  • 另一个前台应用通过绑定到应用的其中一个服务或使用应用的其中一个 content provider 连接到该应用。例如,如果另一个应用绑定到该应用,则该应用处于前台:
    • 输入法
    • 壁纸服务
    • 通知侦听器
    • 语音或文本服务

如果以上条件均不满足,应用即被视为在后台运行。

处于前台时,应用可以自由创建和运行前台和后台服务。进入后台时,在一个持续数分钟的时间窗中,应用仍可以创建和使用服务。在该窗口期结束时,应用将被视为处于空闲状态。此时,系统会停止应用的后台服务,就像应用已经调用服务的 Service.stopSelf() 方法一样。

在某些情况下,系统会将后台应用列入临时许可名单,并持续几分钟。应用被列入许可名单后,可以无限制地启动服务,并且其后台服务也可以运行。处理用户可见的任务(例如,应用)时,应用会被列入许可名单:

在许多情况下,您的应用都可以使用 JobScheduler 作业替换后台服务。例如,CoolPhotoApp 需要检查用户是否已收到朋友分享的照片,即使该应用没有在前台运行也是如此。以前,应用使用的是后台服务,该服务会检查应用的云端存储空间。为了迁移到 Android 8.0(API 级别 26),开发者会将后台服务替换为计划作业,该作业将定期启动,查询服务器,然后退出。

在 Android 8.0 之前,创建前台服务的方式通常是先创建后台服务,然后将该服务提升到前台。Android 8.0 中有一项复杂功能;系统不允许后台应用创建后台服务。因此,Android 8.0 引入了 startForegroundService() 这一新方法,用于在前台启动新服务。系统创建服务后,应用有五秒钟的时间来调用服务的 [startForeground()](/reference/android/app/Service#startForeground(int, android.app.Notification) 方法,以显示新服务的用户可见通知。如果应用在时间限制内未调用 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)或更高版本为目标平台的应用。不过,用户可以通过设置屏幕为任何应用启用这些限制,即使应用以低于 26 的 API 级别为目标平台也是如此。您可能需要更新应用,使其符合这些新限制。

查看您的应用如何使用服务。如果您的应用依赖于在其空闲时在后台运行的服务,您需要替换这些服务。可能的解决方法包括:

  • 如果应用在后台运行时需要创建前台服务,请使用 startForegroundService() 方法,而非 startService()
  • 如果服务容易被用户注意到,请将其设为前台服务。例如,播放音频的服务应始终是前台服务。使用 startForegroundService() 方法(而非 startService())创建服务。
  • 设法使用预定作业实现服务功能。如果服务没有在执行用户可立即注意到的操作,您通常应该能够使用预定作业。
  • 在发生网络事件时,使用 FCM 选择性地唤醒您的应用,而不是在后台轮询。
  • 请推迟后台工作,直至应用在前台正常运行。

查看应用清单中定义的广播接收器。如果您的清单为受影响的隐式广播声明了接收器,您必须替换该接收器。可能的解决方法包括:

  • 通过调用 Context.registerReceiver()(而不是在清单中声明接收器)在运行时创建接收器。
  • 使用预定作业检查条件是否会触发隐式广播。