后台执行限制

每当应用在后台运行时,它都会消耗设备的一些有限资源,例如 RAM。这可能会导致用户体验受损,尤其是在用户使用资源密集型应用(例如玩游戏或观看视频)时。为了改善用户体验,Android 8.0(API 级别 26)对应用在后台运行时可以执行的操作施加了限制。本文档介绍了操作系统中的更改,以及如何更新您的应用以在新的限制下良好运行。

概述

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

为了降低这些问题发生的可能性,Android 8.0 对用户未直接与之交互时应用可以执行的操作施加了限制。应用受到两种方式的限制

  • 后台服务限制:当应用处于空闲状态时,其使用后台服务的方式受到限制。这并不适用于前台服务,前台服务对用户来说更加明显。

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

在大多数情况下,应用可以通过使用 JobScheduler 作业来解决这些限制。这种方法允许应用安排在应用未积极运行时执行工作,但仍然让系统有余地以不影响用户体验的方式安排这些作业;有关更多信息,请参阅 JobScheduler 改进

后台服务限制

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

系统区分前台应用和后台应用。(出于服务限制的目的,后台的定义与内存管理使用的定义不同;应用可能在内存管理方面处于后台,但在启动服务的能力方面处于前台。)如果满足以下任何条件,则应用被视为处于前台

  • 它具有可见活动,无论该活动是已启动还是已暂停。
  • 它具有前台服务。
  • 另一个前台应用已连接到该应用,方法是绑定到其服务之一或使用其内容提供程序之一。例如,如果另一个应用绑定到其,则该应用处于前台
    • 输入法
    • 壁纸服务
    • 通知侦听器
    • 语音或文本服务

如果没有满足上述任何条件,则该应用被视为处于后台。

当应用处于前台时,可以自由创建和运行前台和后台服务。当应用进入后台时,它有一个几分钟的窗口期,在此期间仍允许创建和使用服务。窗口期结束后,应用被视为空闲状态。此时,系统会停止应用的后台服务,就像应用调用了服务的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()在运行时创建接收器,而不是在清单中声明接收器。
  • 使用计划作业检查本应触发隐式广播的条件。