2. 持续定位并实时更新地图标记
持续定位与地图标记实时更新
单次定位只能拿到一个静态坐标,而导航、跑步记录、外勤打卡这类场景需要位置随时间连续变化。本节用 FusedLocationProviderClient 注册持续位置监听,把每次回调的经纬度推给 Google 地图上的 Marker,并让相机跟随移动。核心难点不在"能不能更新",而在于更新频率与电池消耗的平衡——回调越密,轨迹越平滑,但 GPS 与网络定位模块唤醒越频繁,耗电越快。
持续定位的数据链路
持续定位不是"请求一次、返回一次",而是一条从系统定位服务到地图渲染的持续数据流。下图展示了这条链路中每个环节的对象、参数与状态变化,其中 LocationRequest 的三个参数直接决定了回调节奏与耗电水平。
注册持续监听的关键实现
持续定位的注册分三步:构建 LocationRequest、实现 LocationCallback、调用 requestLocationUpdates。下面代码可直接嵌入 Activity 的 onCreate 或按钮点击事件中,前提是已获得 ACCESS_FINE_LOCATION 权限。
// 1. 构建定位请求:平衡精度与功耗
LocationRequest locationRequest = new LocationRequest.Builder(
Priority.PRIORITY_BALANCED_POWER_ACCURACY, // 平衡模式
5000L) // 期望间隔 5 秒
.setMinUpdateIntervalMillis(2000L) // 最快 2 秒,防止过密回调
.setMinUpdateDistanceMeters(5f) // 移动不足 5 米不回调
.build();
// 2. 实现回调:每次位置变化时更新地图标记
LocationCallback locationCallback = new LocationCallback() {
@Override
public void onLocationResult(@NonNull LocationResult result) {
Location location = result.getLastLocation();
if (location == null) return;
LatLng currentLatLng = new LatLng(
location.getLatitude(),
location.getLongitude());
// 移动标记而非新建,避免地图上堆积大量 Marker
if (currentMarker == null) {
currentMarker = googleMap.addMarker(new MarkerOptions()
.position(currentLatLng)
.title("当前位置"));
} else {
currentMarker.setPosition(currentLatLng);
}
// 相机平滑跟随,避免每次跳变
googleMap.animateCamera(
CameraUpdateFactory.newLatLng(currentLatLng), 800, null);
}
};
// 3. 注册监听(需在权限已授予后调用)
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()); // 回调在主线程,可直接操作 UI
为什么用
setPosition 而不是反复 addMarker? 每次回调都新建 Marker 会导致地图上出现成百上千个重叠标记,内存和渲染开销急剧上升。正确做法是持有一个 Marker 引用,只更新其位置。
更新频率与电池消耗的平衡策略
三个参数共同决定回调密度与耗电水平,实际项目中应根据场景选择组合:
| 场景 | priority | interval | minUpdateDistance | 预期耗电 |
|---|---|---|---|---|
| 跑步轨迹记录 | PRIORITY_HIGH_ACCURACY | 2000 ms | 3 m | 高(约 8~12%/h) |
| 外卖骑手位置上报 | PRIORITY_BALANCED_POWER_ACCURACY | 5000 ms | 10 m | 中(约 4~6%/h) |
| 后台签到/打卡 | PRIORITY_LOW_POWER | 30000 ms | 50 m | 低(约 1~2%/h) |
不要忽略
setMinUpdateDistanceMeters。 当设备静止时,GPS 仍会因信号漂移产生微小坐标变化。若不设最小位移阈值,即使人没动,回调也会持续触发,白白消耗电量。设置 5~10 米阈值可有效过滤静止抖动。
生命周期管理与资源释放
持续监听必须在页面不可见时停止,否则应用退到后台仍会持续定位,既耗电又可能被系统限制。在 onPause 中移除监听,在 onResume 中重新注册:
@Override
protected void onResume() {
super.onResume();
if (hasLocationPermission()) {
fusedLocationClient.requestLocationUpdates(
locationRequest, locationCallback, Looper.getMainLooper());
}
}
@Override
protected void onPause() {
super.onPause();
// 移除监听,停止位置回调
fusedLocationClient.removeLocationUpdates(locationCallback);
}
验证与观察要点
- 标记是否移动: 在室外步行 20~30 米,观察地图上的标记是否随位置变化平滑移动,而非跳跃式闪现。
- 回调频率是否符合预期: 在
onLocationResult中打印时间戳,确认相邻两次回调间隔接近设定的interval(允许系统调度偏差)。 - 静止时是否停止回调: 将手机静置桌面,观察日志中是否仍有位置回调。若持续触发,检查
setMinUpdateDistanceMeters是否生效。 - 退出页面后是否停止: 按 Home 键退到后台,确认日志中不再出现新的位置回调,说明
removeLocationUpdates已生效。
本节核心结论: 持续定位的更新频率由
interval、fastestInterval、minUpdateDistance 和 priority 四个参数共同控制。地图标记应复用同一个 Marker 对象并调用 setPosition,相机用 animateCamera 平滑跟随。页面不可见时必须调用 removeLocationUpdates 释放监听,这是控制电池消耗最关键的一步。