系统对后台工作的限制

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

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

用户发起的限制

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

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

  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 的电量使用文档。