系统对后台任务的限制

后台进程可能会耗费大量内存和电池电量。例如,某一隐式广播可能会启动许多已注册监听它的后台进程,即使这些进程可能并没有执行很多任务。这会对设备性能和用户体验产生重大影响。

为避免系统限制,请确保您为后台任务使用正确的 API。后台任务概览文档可帮助您选择适合您需求的 API。

用户发起的限制

如果应用出现 Android Vitals 中描述的一些不良行为, 系统会提示用户限制该应用对系统资源的访问。

如果系统发现应用消耗的资源过多,就会通知用户,并为用户提供限制应用操作的选项。 可触发此类通知的行为包括:

  1. 唤醒锁定过多:屏幕关闭时持续 1 小时的 1 次部分唤醒锁定
  2. 后台服务过多:应用的目标 API 级别低于 26 且后台服务过多

施加的确切限制由设备制造商决定。例如,在 AOSP build 中,受限应用不能运行作业、触发闹钟或使用网络,除非该应用在前台运行。

对接收网络活动广播的限制

如果应用在其清单中注册接收 CONNECTIVITY_ACTION 广播,则不会收到该广播,且依赖于此广播的进程 不会启动。如果应用需要监听网络更改或在设备连接到不按流量计费的网络时执行批量网络活动,这种限制可能会给这些应用带来问题。Android 框架中已经存在多种解决此限制的解决方案,但您需要根据希望应用完成的操作选择合适的解决方案。

在不按流量计费的连接上调度工作

构建 WorkRequest 时,添加 NetworkType.UNMETERED Constraint

fun scheduleWork(context: Context) {
    val workManager = WorkManager.getInstance(context)
    val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
       .setConstraints(
           Constraints.Builder()
               .setRequiredNetworkType(NetworkType.UNMETERED)
               .build()
           )
       .build()

    workManager.enqueue(workRequest)
}

当满足工作条件时,您的应用会收到一个回调,以运行 doWork()方法,该方法位于指定的Worker类中。

在应用运行时监控网络连接

正在运行的应用仍然可以通过 已注册的 BroadcastReceiver 监听 CONNECTIVITY_CHANGE。不过,ConnectivityManager API 提供一个更强大的方法,用于仅在满足指定的网络 条件时请求回调。

NetworkRequest 对象在 NetworkCapabilities 方面定义网络回调的参数。您可以使用 NetworkRequest.Builder 类创建 NetworkRequest 对象 。registerNetworkCallback 随后会将 NetworkRequest 对象传递给系统。满足网络 条件时,应用会收到回调,以执行其 onAvailable() 类中定义的 ConnectivityManager.NetworkCallback 方法。

在应用退出或调用 unregisterNetworkCallback()之前,应用会继续接收回调。

对接收图片和视频广播的限制

应用无法发送或接收 ACTION_NEW_PICTUREACTION_NEW_VIDEO 广播。当系统必须唤醒多个应用来处理新的图片或视频时,此限制有助于减轻对性能和用户体验的影响。

确定哪些内容授权方触发了工作

WorkerParameters 允许您的应用接收有关哪些内容授权方和 URI 触发了该工作的有用信息:

List<Uri> getTriggeredContentUris()

返回已触发工作的 URI 列表。如果没有 URI 触发工作(例如,工作因截止日期或某种其他原因而触发),或发生更改的 URI 数量大于 50,则该列表为空。

List<String> getTriggeredContentAuthorities()

返回已触发工作的内容授权方的字符串列表。如果返回的列表不为空,请使用 getTriggeredContentUris() 检索关于哪些 URI 发生了更改的详细信息。

以下示例代码会替换 CoroutineWorker.doWork() 方法 并记录已触发作业的内容授权方和 URI:

class MyWorker(
    appContext: Context,
    params: WorkerParameters
): CoroutineWorker(appContext, params)
    override suspend fun doWork(): Result {
        StringBuilder().apply {
            append("Media content has changed:\n")
            params.triggeredContentAuthorities
                .takeIf { it.isNotEmpty() }
                ?.let { authorities ->
                    append("Authorities: ${authorities.joinToString(", ")}\n")
                    append(params.triggeredContentUris.joinToString("\n"))
                } ?: append("(No content)")
            Log.i(TAG, toString())
        }
        return Result.success()
    }
}

在系统限制下测试应用

可以对应用进行优化,使其能够在内存不足的设备上或在内存不足的条件下运行,从而改善性能和用户体验。如果取消对后台服务和在清单中注册的隐式广播接收器的依赖,将有助于提高您的应用在此类设备上的运行效率。建议您优化应用,使其能够在完全不使用这些后台进程的情况下运行。

一些额外的 Android 调试桥 (ADB) 命令可以帮助您在这些后台进程停用的情况下测试应用 行为:

  • 如需模拟隐式广播和后台服务不可用的情况,请输入以下命令:

    $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore

  • 如需重新启用隐式广播和后台服务,请输入以下命令:

    $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow

进一步优化应用

如需了解其他优化后台任务行为的有效方法,请参阅 针对任务调度 API 优化电池用量 文档。