优化电池位置

Android 8.0(API 级别 26)中引入的 后台位置限制 使人们重新关注位置服务使用如何影响电池电量消耗的问题。本页介绍了一些位置服务最佳实践以及您现在可以采取哪些措施来提高应用程序的电池效率。无论应用程序运行在哪个平台版本上,应用这些最佳实践都有利于您的应用程序。

Android 8.0 中的后台位置限制引入了以下更改

  • 限制后台位置收集,位置计算和交付每小时仅进行几次。
  • Wi-Fi 扫描更加保守,当设备保持连接到相同的静态接入点时,不会计算位置更新。
  • 地理围栏响应能力从数十秒更改为大约两分钟。此更改显着改善了电池性能 - 在某些设备上提高了 10 倍。

本页假设您使用的是 Google 位置服务 API,与 框架位置 API 相比,这些 API 提供更高的准确度并减少了电池消耗。特别是,本页假设您熟悉 融合位置提供程序 API,它结合了来自 GPS、Wi-Fi 和蜂窝网络的信号,以及加速度计、陀螺仪、磁力计和其他传感器。您还应该熟悉 地理围栏 API,它构建在融合位置提供程序 API 之上,并且针对电池性能进行了优化。

了解电池电量消耗

位置收集和电池电量消耗在以下方面直接相关

  • 准确度:位置数据的精度。通常,准确度越高,电池电量消耗就越高。
  • 频率:计算位置的频率。计算位置越频繁,使用的电池电量就越多。
  • 延迟:位置数据交付的速度。延迟越短,通常需要的电池电量就越多。

准确度

您可以使用 setPriority() 方法指定位置准确度,将以下值之一作为参数传递

  • PRIORITY_HIGH_ACCURACY 提供尽可能准确的位置,该位置使用尽可能多的输入进行计算(它启用 GPS、Wi-Fi 和蜂窝网络,并使用各种 传感器),可能会导致明显的电池电量消耗。
  • PRIORITY_BALANCED_POWER_ACCURACY 在优化功率的同时提供准确的位置。很少使用 GPS。通常使用 Wi-Fi 和蜂窝网络信息的组合来计算设备位置。
  • PRIORITY_LOW_POWER 主要依赖于蜂窝塔,避免 GPS 和 Wi-Fi 输入,提供较粗略(城市级)的准确度,同时电池消耗最少。
  • PRIORITY_NO_POWER 从已经计算出位置的其他应用程序被动接收位置。

大多数应用程序的位置需求可以使用平衡功耗或低功耗选项来满足。高精度应保留用于在前台运行且需要实时位置更新的应用程序(例如,地图应用程序)。

频率

您可以使用两种方法指定位置频率

  • 使用 setinterval() 方法指定为您的应用程序计算位置的间隔。
  • 使用 setFastestInterval() 指定为其他应用程序计算的位置交付到您的应用程序的间隔。

使用 setInterval() 时,应传递尽可能大的值。对于后台位置收集尤其如此,后台位置收集通常是造成意外电池电量消耗的原因。使用几秒钟的间隔应保留用于前台用例。Android 8.0 中引入的后台位置限制强制执行这些策略,但您的应用程序应努力在 Android 7.0 或更低版本的设备上强制执行这些策略。

延迟

您可以使用 setMaxWaitTime() 方法指定延迟,通常传递的值是 setInterval() 方法中指定的间隔的几倍。此设置会延迟位置交付,并且可能会以批处理形式交付多个位置更新。这两个更改有助于最大程度地减少电池消耗。

如果您的应用程序不需要立即进行位置更新,您应该将尽可能大的值传递给 setMaxWaitTime() 方法,有效地用延迟换取更多数据和电池效率。

使用地理围栏时,应用程序应将较大的值传递到 setNotificationResponsiveness() 方法以节省电量。建议使用 5 分钟或更长时间的值。

位置用例

本节介绍了一些典型的位置收集场景,以及针对地理围栏和融合位置提供程序 API 的最佳使用建议。

用户可见或前台更新

例如:地图应用需要频繁、准确的更新,延迟非常低。所有更新都在前台进行:用户开始一项活动,使用位置数据,然后在短时间内停止活动。

使用 setPriority() 方法,其值为 PRIORITY_HIGH_ACCURACYPRIORITY_BALANCED_POWER_ACCURACY.

setInterval() 方法中指定的间隔取决于用例:对于实时场景,将值设置为几秒钟;否则,限制为几分钟(建议大约两分钟或更长时间,以最大程度地减少电池消耗)。

了解设备的位置

例如:天气应用想要了解设备的位置。

使用 getLastLocation() 方法,该方法返回最近可用的位置(在极少数情况下可能为 null)。此方法提供了一种获取位置的简单方法,不会产生与主动请求位置更新相关的成本。与 isLocationAvailable() 方法结合使用,该方法在 getLastLocation() 返回的位置合理更新时返回 true

在用户位于特定位置时开始更新

例如:在用户距离工作地点、住所或其他位置一定距离内时请求更新。

地理围栏 与融合位置提供程序更新结合使用。在应用接收到地理围栏进入触发器时请求更新,并在应用接收到地理围栏退出触发器时删除更新。这可确保应用仅在用户进入定义区域时获取更细粒度的位置更新。

此场景的典型工作流程可能包括在地理围栏进入转换时显示通知,并启动一个包含请求更新代码的活动,当用户点击通知时,该活动将执行该代码。

根据用户的活动状态开始更新

例如:仅在用户驾驶或骑自行车时请求更新。

活动识别 API 与融合位置提供程序更新结合使用。在检测到目标活动时请求更新,并在用户停止执行该活动时删除更新。

此用例的典型工作流程可能包括为检测到的活动显示通知,并启动一个包含请求更新代码的活动,当用户点击通知时,该活动将执行该代码。

与地理区域绑定的长时间运行的后台位置更新

例如:用户希望在设备靠近零售商时收到通知。

这是使用地理围栏的绝佳用例。由于用例几乎肯定会涉及后台位置,因此使用 addGeofences(GeofencingRequest, PendingIntent) 方法。

应设置以下配置选项

  • 如果要跟踪停留转换,请使用 setLoiteringDelay() 方法,传递一个大约五分钟或更小的值。

  • 使用 setNotificationResponsiveness(),传递一个大约五分钟的值。但是,如果您的应用能够处理额外的响应延迟,请考虑使用大约十分钟的值。

应用一次最多只能注册 100 个地理围栏。在应用想要跟踪大量零售商选项的用例中,应用可能希望注册大型地理围栏(在城市级别),并动态注册较小的地理围栏(用于城市内的位置),以用于大型地理围栏内的商店。当用户进入大型地理围栏时,可以添加较小的地理围栏;当用户退出大型地理围栏时,可以删除较小的地理围栏,并且可以为新区域重新注册地理围栏。

没有可见应用组件的长时间运行的后台位置更新

例如:被动跟踪位置的应用

如果可能,请使用 setPriority() 方法,其值为 PRIORITY_NO_POWER,因为它几乎不会消耗电池。如果无法使用 PRIORITY_NO_POWER,请使用 PRIORITY_BALANCED_POWER_ACCURACYPRIORITY_LOW_POWER,但避免在持续的后台工作中使用 PRIORITY_HIGH_ACCURACY,因为此选项会大量消耗电池。

如果需要更多位置数据,请通过调用 setFastestInterval() 方法传递一个比传递给 setInterval() 的值更小的值来使用被动位置。与 PRIORITY_NO_POWER 选项结合使用,被动位置可以按需免费提供其他应用计算的位置。

通过增加一些延迟来适度频率,使用 setMaxWaitTime() 方法。例如,如果使用 setinterval() 方法,其值为大约十分钟,则应考虑使用 setMaxWaitTime(),其值为 30 到 60 分钟之间。使用这些选项,大约每十分钟为您的应用计算一次位置,但您的设备大约每 30 到 60 分钟唤醒一次,并提供一些可用作批量更新的位置数据。这种方法以延迟换取更多可用数据和更好的电池性能。

用户与其他应用交互时的频繁高精度更新

例如:导航或健身应用在用户关闭屏幕或打开其他应用时继续运行。

使用前台服务。如果您的应用可能代表用户执行昂贵的操作,那么让用户了解该操作是一种推荐的最佳做法。前台服务需要一个持续的通知。有关更多信息,请参阅 通知概述.

位置最佳实践

实施本节中的最佳实践有助于降低应用的电池消耗。

删除位置更新

不必要的电池消耗的一个常见来源是,在不再需要位置更新时未能删除它们。例如,当活动的 onStart()onResume() 生命周期方法包含对 requestlocationUpdates() 的调用,而没有在 onPause()onStop() 生命周期方法中进行相应的 removeLocationUpdates() 调用时,就会出现这种情况。

您可以使用生命周期感知组件来更好地管理应用中活动的生命周期。有关更多信息,请参阅 使用生命周期感知组件处理生命周期.

设置超时

为了防止电池过度消耗,请在位置更新应停止时设置合理的超时。超时确保更新不会无限期地继续,并且它可以保护应用在请求更新但未删除更新的情况下(例如,由于代码中的错误)。

对于融合位置提供程序请求,通过调用 setExpirationDuration() 来添加超时,该方法接收一个参数,该参数表示自上次调用该方法以来的毫秒数。您还可以通过调用 setExpirationTime() 来添加超时,该方法接收一个参数,该参数表示自系统上次启动以来的毫秒数。

要向地理围栏位置请求添加超时,请调用 setExpirationDuration() 方法。

批量请求

对于所有非前台用例,将多个请求一起打包。您可以使用 setInterval() 方法来指定您希望计算位置的间隔。然后,使用 setMaxWaitTime() 方法来设置向应用传递位置的间隔。传递给 setMaxWaitTime() 方法的值应是传递给 setInterval() 方法的值的倍数。例如,请考虑以下位置请求

Kotlin

val request = LocationRequest()
request.setInterval(10 * 60 * 1000)
request.setMaxWaitTime(60 * 60 * 1000)

Java

LocationRequest request = new LocationRequest();
request.setInterval(10 * 60 * 1000);
request.setMaxWaitTime(60 * 60 * 1000);

在这种情况下,大约每十分钟计算一次位置,并且大约每小时以批量的形式传递大约六个位置数据点。虽然您仍然每十分钟左右获得一次位置更新,但您可以节省电池,因为您的设备大约每小时只唤醒一次。

使用被动位置更新

在后台用例中,最好限制位置更新。Android 8.0 限制会强制执行此做法,但运行在较旧设备上的应用应尽可能限制后台位置。

当您的应用处于后台时,另一个应用可能正在前台频繁地请求位置更新。位置服务会将这些更新提供给您的应用。请考虑以下位置请求,该请求会按需使用位置数据

Kotlin

val request = LocationRequest()
request.setInterval(15 * 60 * 1000)
request.setFastestInterval(2 * 60 * 1000)

Java

LocationRequest request = new LocationRequest();
request.setInterval(15 * 60 * 1000);
request.setFastestInterval(2 * 60 * 1000);

在前面的示例中,大约每 15 分钟为您的应用计算一次位置。如果其他应用请求位置,则数据将以最长两分钟的间隔提供给您的应用。

虽然被动地使用位置信息不会消耗电池电量,但请格外注意,在接收位置数据触发昂贵的 CPU 或 I/O 操作的情况下,请格外小心。为了最大限度地降低电池成本,在 setFastestInterval() 中指定的间隔时间不应该太小。

通过遵循本页面的建议,您可以显著提高用户设备的电池性能。您的用户不太可能删除那些不会耗尽其电池的应用。