后台执行限制

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

概览

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

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

  • 后台服务限制:当应用处于空闲状态时,其后台服务的使用存在限制。这不适用于前台服务,因为前台服务更容易引起用户注意。

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

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

后台服务限制

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

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

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

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

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

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

在许多情况下,您的应用可以使用 JobScheduler 作业替换后台服务。例如,Cool PhotoApp 需要检查用户是否已收到朋友分享的照片,即使应用没有在前台运行也是如此。以前,应用使用的是后台服务,该服务会检查应用的云端存储空间。为了迁移到 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() 在运行时创建接收器,而不是在清单中声明接收器。
  • 使用预定作业检查条件是否会触发隐式广播。