优化电池续航的定位

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

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

  • 后台位置收集受到限制,位置仅每隔几个小时计算和传递一次。
  • Wi-Fi 扫描更加保守,并且当设备保持连接到同一静态接入点时不会计算位置更新。
  • 地理围栏响应时间从几十秒变为大约两分钟。此更改显着提高了电池性能,在某些设备上可提高高达 10 倍。

此页面假设您正在使用Google 位置服务 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() 方法以节省电量。建议使用五分钟或更长的时间。

位置使用案例

本节介绍了一些典型的位置收集场景,以及关于如何优化使用地理围栏和融合位置提供程序 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() 方法并将值设置为大约 10 分钟,则应考虑使用 setMaxWaitTime() 方法,并将值设置为 30 到 60 分钟之间。使用这些选项,大约每 10 分钟为您的应用计算一次位置,但应用仅每 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() 中指定的间隔不应太小。

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