2. 持续定位并实时更新地图标记

持续定位并实时更新地图标记 学习卡片

持续定位与地图标记实时更新

单次定位只能拿到一个静态坐标,而导航、跑步记录、外勤打卡这类场景需要位置随时间连续变化。本节用 FusedLocationProviderClient 注册持续位置监听,把每次回调的经纬度推给 Google 地图上的 Marker,并让相机跟随移动。核心难点不在"能不能更新",而在于更新频率与电池消耗的平衡——回调越密,轨迹越平滑,但 GPS 与网络定位模块唤醒越频繁,耗电越快。

持续定位的数据链路

持续定位不是"请求一次、返回一次",而是一条从系统定位服务到地图渲染的持续数据流。下图展示了这条链路中每个环节的对象、参数与状态变化,其中 LocationRequest 的三个参数直接决定了回调节奏与耗电水平。

定位源 GPS 卫星 Wi-Fi / 基站 传感器融合 精度:3~50 m FusedLocationProvider LocationRequest 参数 interval = 5000 ms fastestInterval = 2000 ms priority = PRIORITY_BALANCED LocationCallback onLocationResult(result) result.lastLocation → latitude / longitude → accuracy / time 原始信号 按间隔推送 Google Map 渲染 marker.position = LatLng camera.animate(LatLng) moveCamera / animateCamera LatLng 更新 电池消耗 interval ↓ → 耗电 ↑ priority ↑ → 耗电 ↑ 平衡点:5s / BALANCED 参数约束 位置回调时间轴(interval = 5s) t=0s (39.90,116.40) t=5s (39.901,116.401) t=10s (39.902,116.402) t=15s (39.903,116.403) t=20s (39.904,116.404) t=25s (39.905,116.405) t=30s (39.906,116.406) t=35s (39.907,116.407)

注册持续监听的关键实现

持续定位的注册分三步:构建 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 释放监听,这是控制电池消耗最关键的一步。