系统对后台工作的限制

后台进程可能非常占用内存和电池电量。例如,隐式广播可能会启动许多已注册以监听它的后台进程,即使这些进程可能不会执行太多工作。这可能会对设备性能和用户体验产生重大影响。

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

用户启动的限制

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

如果系统注意到应用程序正在消耗过多的资源,它会通知用户,并让用户选择限制应用程序的操作。可能会触发通知的行为包括

  1. 过度唤醒锁定:屏幕关闭时,一个部分唤醒锁定保持一小时
  2. 过多的后台服务:如果应用程序面向 API 级别低于 26,并且具有过多的后台服务

实施的精确限制由设备制造商确定。例如,在 AOSP 构建中,受限制的应用程序不能运行作业、触发警报或使用网络,除非应用程序处于前台。

接收网络活动广播的限制

如果应用程序在其清单中注册以接收 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)
}

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

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

正在运行的应用程序仍然可以使用注册的 BroadcastReceiver 监听 CONNECTIVITY_CHANGE。但是,ConnectivityManager API 提供了一种更强大的方法,仅在满足特定网络条件时请求回调。

NetworkRequest 对象根据 NetworkCapabilities 定义网络回调的参数。您可以使用 NetworkRequest.Builder 类创建 NetworkRequest 对象。然后,registerNetworkCallbackNetworkRequest 对象传递给系统。满足网络条件时,应用程序会收到一个回调以执行其 ConnectivityManager.NetworkCallback 类中定义的 onAvailable() 方法。

应用程序会继续接收回调,直到应用程序退出或调用 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 优化电池使用 文档。