3、获取位置:LocationManager基本用法、getLastKnownLocation详解、requestLocationUpdates回调机制

说实话,LocationManager 这玩意儿,是 Android 定位的老祖宗了。虽然现在 Google 推 FusedLocationProvider,但很多老项目、车机、或者对 Google Play 服务有依赖限制的场景,还是得靠它。我个人习惯是,新项目用 Fused,但遇到兼容性需求,回头用 LocationManager 反而更稳。

这一章,咱们就把它掰开揉碎了讲。你想想看,一个 App 要拿到用户的位置,无非两种方式:要么问系统「你最近一次知道的位置在哪」,要么让系统「有位置更新了你就告诉我」。前者是 getLastKnownLocation,后者是 requestLocationUpdates。咱们一个一个来。

3.1 LocationManager 基本用法

要拿到 LocationManager,得先通过系统服务获取。代码很简单:

LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

嗯,这里要注意。拿到这个对象之后,你才能去操作位置相关的功能。它支持多种 Provider,常见的有:

Provider 名称 说明 典型精度
GPS_PROVIDER 卫星定位,耗电高,精度高 3-10 米
NETWORK_PROVIDER 基站/WiFi 定位,省电,精度一般 50-200 米
PASSIVE_PROVIDER 被动接收其他应用的位置更新 取决于来源

我在项目中遇到过,有些国产手机对 NETWORK_PROVIDER 支持很差,甚至直接返回 null。所以,我一般会先检查 Provider 是否可用:

boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
boolean networkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

如果两个都不可用,那就得引导用户去设置里打开位置开关。别指望代码能硬开,Android 不允许。

小技巧: 我习惯先检查 NETWORK_PROVIDER,再检查 GPS_PROVIDER。因为网络定位启动快,能快速拿到一个粗略位置,等 GPS 锁定后再更新,用户体验会好很多。

3.2 getLastKnownLocation 详解

这个方法,说白了就是「查缓存」。系统会保存每个 Provider 最后一次获取到的位置。你调用它,就能拿到这个缓存值。

Location lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

但是!这里有个大坑。我曾经在一个户外导航 App 里,直接拿这个值当当前位置,结果用户反馈定位不准。为什么呢?因为 getLastKnownLocation 返回的位置可能是几分钟前、甚至几小时前的。如果用户刚从地铁站出来,这个位置可能还在地铁站里。

避坑指南: 我曾经因为没判断时间戳,导致用户位置显示在旧地点。一定要检查 location.getTime() 与当前时间的差值。如果超过 2 分钟,建议不要使用,而是主动发起一次 requestLocationUpdates。

正确的做法是:

Location lastKnown = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (lastKnown != null) {
    long now = System.currentTimeMillis();
    long age = now - lastKnown.getTime();
    if (age < 2 * 60 * 1000) { // 2分钟以内
        // 可以使用
    } else {
        // 太旧了,重新请求
    }
}

另外,getLastKnownLocation 可能返回 null。尤其是刚开机、或者用户从未开启过定位时。所以,别指望它一定能拿到值。

3.3 requestLocationUpdates 回调机制

这才是实时定位的核心。你告诉系统:「每隔一段时间,或者每移动一段距离,你就通知我一次」。系统会通过回调把最新的 Location 对象传给你。

基本用法:

locationManager.requestLocationUpdates(
    LocationManager.GPS_PROVIDER,   // 使用 GPS
    5000,                           // 最小时间间隔,单位毫秒
    10,                             // 最小距离变化,单位米
    locationListener                // 回调监听器
);

这里有两个参数要特别注意:

  • minTime:5 秒。意思是至少 5 秒才回调一次。但注意,这不是精确的。系统可能会因为省电策略,把多个更新合并。
  • minDistance:10 米。意思是位置变化超过 10 米才回调。如果设为 0,则只要有新位置就回调,非常耗电。

LocationListener 是一个接口,你需要实现它的四个方法:

LocationListener locationListener = new LocationListener() {
    @Override
    public void onLocationChanged(Location location) {
        // 位置更新了!这里拿到最新的 location
        double lat = location.getLatitude();
        double lng = location.getLongitude();
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        // Provider 状态变化,比如 GPS 信号丢失
    }

    @Override
    public void onProviderEnabled(String provider) {
        // 用户打开了定位
    }

    @Override
    public void onProviderDisabled(String provider) {
        // 用户关闭了定位
    }
};

你想想看,onLocationChanged 是最常用的。但 onProviderDisabled 也很重要。我曾经有个 App,用户关闭 GPS 后,回调就停了,但 UI 上还显示着旧位置。后来我加了 onProviderDisabled 的处理,提示用户「定位已关闭」,体验就好多了。

核心要点: requestLocationUpdates 会持续回调,直到你调用 removeUpdates()。别忘了在 Activity 的 onPause 或 onDestroy 里取消注册,否则会内存泄漏。
@Override
protected void onPause() {
    super.onPause();
    locationManager.removeUpdates(locationListener);
}

3.4 知识体系图

下面这张图,帮你理清 LocationManager 的核心逻辑:

LocationManager 核心流程 getSystemService isProviderEnabled 检查 getLastKnownLocation requestLocationUpdates 返回缓存位置(可能为 null) onLocationChanged 持续回调 检查时间戳,避免使用旧数据 记得 removeUpdates 防泄漏

3.5 实际项目中的选择

我个人习惯是,如果 App 只需要一个大概位置(比如显示天气),用 getLastKnownLocation 就够了。但如果要做导航、运动轨迹记录,必须用 requestLocationUpdates。

而且,我建议你同时注册两个 Provider:先用 NETWORK_PROVIDER 快速拿到一个位置,等 GPS 锁定后,再切换到 GPS 的高精度数据。这样用户不会觉得「怎么等了 10 秒还没定位」。说白了,就是先用缓存顶一下,再用实时数据更新。

经验之谈: 我曾经在一个骑行 App 里,把 minDistance 设为 5 米,结果每秒钟回调好几次,电量哗哗地掉。后来改成 20 米,体验反而更好。别为了「实时」牺牲续航,用户会骂的。

好了,这一章的内容就这些。LocationManager 虽然老,但基本功扎实。下一章咱们会聊聊更现代的 FusedLocationProvider,但如果你把 LocationManager 吃透了,理解 Fused 会轻松很多。


公众号:蓝海资料掘金营,微信 deep3321