系统对后台工作的限制

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

为避免系统限制,请确保为您的后台任务使用正确的 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()

返回已触发工作的 Content Authority 字符串列表。如果返回的列表不为空,请使用 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 的电池使用 文档。