后台优化

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

用户发起的限制

如果应用出现 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)
}

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

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

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

NetworkRequest 对象根据 NetworkCapabilities 定义网络回调的参数。您可以使用 NetworkRequest.Builder 类创建 NetworkRequest 对象。[registerNetworkCallback][10] 然后向系统传递 NetworkRequest 对象。当满足网络条件时,应用会收到回调,以执行其 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

[4]:/reference/android/content/Context.html#registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter)

[10]:/reference/android/net/ConnectivityManager.html#registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback)