6、位置提供器:GPS_PROVIDER详解、NETWORK_PROVIDER详解、PASSIVE_PROVIDER被动监听

位置提供器,说白了就是系统获取你位置的「渠道」。Android 提供了三种核心渠道:GPS、网络和被动监听。每种都有自己的脾气和适用场景。我刚开始做定位功能时,以为 GPS 就是万能的,结果在室内直接翻车。嗯,今天咱们就把这三个家伙彻底聊透。

GPS_PROVIDER:精度之王,但很挑环境

GPS_PROVIDER 用的是卫星信号。精度通常在 5-10 米,甚至更好。但有个致命弱点——它需要「看到天」。

核心特点:
  • 精度高:室外空旷地带可达 3-5 米
  • 耗电大:持续定位的话,半天就能把电池干光
  • 冷启动慢:第一次定位可能需要 30 秒到 2 分钟
  • 室内基本废掉:信号被楼板挡住,定位会漂到离谱

我在项目中遇到过一个问题:用户在地下停车场打开导航,GPS 死活定不到位。后来我加了网络定位做兜底,才解决了这个痛点。

NETWORK_PROVIDER:室内救星,但精度有限

NETWORK_PROVIDER 依赖基站和 Wi-Fi 热点来估算位置。它不需要卫星信号,所以室内也能用。但精度嘛……看运气。

场景 精度范围 备注
城市密集区(多基站) 50-200 米 基站密度越高,精度越好
郊区(基站稀疏) 500-2000 米 有时候能偏到隔壁镇
Wi-Fi 辅助定位 20-50 米 如果 Google 有该 Wi-Fi 的数据库

你想想看,如果用户在大商场里,GPS 根本用不了。这时候 NETWORK_PROVIDER 就是唯一的救命稻草。我曾经做过一个商场导购 App,室内定位全靠它撑场面。

我的建议:不要单独依赖 NETWORK_PROVIDER。把它当作 GPS 的补充,而不是替代。在室内场景下,优先用网络定位,等用户走到室外再切回 GPS。

PASSIVE_PROVIDER:不费电的「偷听」模式

PASSIVE_PROVIDER 很有意思。它自己不主动请求位置更新,而是「偷听」其他应用或系统服务发来的位置广播。说白了,就是蹭别人的定位结果。

为什么会有这种模式?我举个例子:你的 App 不需要实时定位,但想偶尔知道用户大概在哪。如果自己开 GPS,太费电。用 PASSIVE_PROVIDER,就能白嫖系统里其他 App 的定位结果。

适用场景:
  • 后台轻量级位置感知(比如天气 App 自动更新城市)
  • 省电优先,精度其次的功能
  • 配合其他 Provider 做降级策略

但要注意,PASSIVE_PROVIDER 的更新频率完全不可控。如果系统里没有其他 App 在定位,你可能永远收不到回调。我曾经踩过这个坑——写了个后台服务用 PASSIVE_PROVIDER 监听位置,结果用户手机里只有我一个定位 App,回调一次都没触发。

避坑指南:PASSIVE_PROVIDER 不能作为主要定位手段。它只适合做「锦上添花」的辅助功能。如果你需要可靠的位置更新,请老老实实用 GPS 或 NETWORK_PROVIDER。

三种 Provider 的对比与选择

我把它们放在一起对比,你一眼就能看出区别:

特性 GPS_PROVIDER NETWORK_PROVIDER PASSIVE_PROVIDER
精度 高(3-10 米) 中低(50-2000 米) 取决于来源
耗电 极低
室内可用 取决于来源
启动速度 慢(冷启动) 即时(有来源时)
控制权 完全可控 完全可控 不可控

我个人习惯的做法是:室外用 GPS,室内用网络,后台轻量场景用被动。但别死板,具体怎么选要看你的业务场景。

核心逻辑流程图

下面这张图展示了三种 Provider 的定位流程和协作关系:

位置提供器核心逻辑流程图 GPS_PROVIDER NETWORK_PROVIDER PASSIVE_PROVIDER 室外空旷环境 室内/城市环境 监听系统广播 LocationManager 统一调度 位置回调:onLocationChanged(Location) 包含经纬度、精度、速度、时间戳等

代码实战:如何注册和切换 Provider

光说不练假把式。下面这段代码展示了如何同时注册 GPS 和网络 Provider,并做智能切换:

// 获取 LocationManager
LocationManager locationManager = 
    (LocationManager) getSystemService(Context.LOCATION_SERVICE);

// 创建位置监听器
LocationListener locationListener = new LocationListener() {
    @Override
    public void onLocationChanged(Location location) {
        String provider = location.getProvider();
        float accuracy = location.getAccuracy();
        
        // 如果 GPS 精度优于 20 米,优先使用
        if (GPS_PROVIDER.equals(provider) && accuracy < 20) {
            updateUI(location);
            return;
        }
        
        // 如果网络定位精度优于 100 米,也可以接受
        if (NETWORK_PROVIDER.equals(provider) && accuracy < 100) {
            updateUI(location);
        }
    }
};

// 注册 GPS(每 5 秒更新一次,最小位移 10 米)
locationManager.requestLocationUpdates(
    LocationManager.GPS_PROVIDER, 
    5000, 
    10, 
    locationListener
);

// 注册网络定位(每 10 秒更新一次,最小位移 50 米)
locationManager.requestLocationUpdates(
    LocationManager.NETWORK_PROVIDER, 
    10000, 
    50, 
    locationListener
);

// 注册被动监听(不设时间间隔,完全被动)
locationManager.requestLocationUpdates(
    LocationManager.PASSIVE_PROVIDER, 
    0, 
    0, 
    locationListener
);
我的经验:同时注册多个 Provider 时,记得在 onPause() 或 onDestroy() 里移除监听。否则 Activity 销毁了还在定位,既费电又可能引发内存泄漏。我曾经因为这个被用户投诉 App 耗电太快,查了半天才发现是忘记移除监听器了。

Provider 的优先级策略

实际项目中,我一般这样排优先级:

  1. GPS 优先:如果 GPS 定位成功且精度达标,直接使用
  2. 网络兜底:GPS 失败或精度太差时,用网络定位结果
  3. 被动辅助:后台场景下,用被动监听做轻量级位置感知

你想想看,如果用户从室外走进室内,GPS 信号会逐渐变差。这时候如果代码写死了只用 GPS,定位就会卡住。我做过一个骑行 App,就遇到过这种场景——用户骑进隧道,GPS 信号断了,导航直接卡死。后来加了网络定位做降级,问题才解决。

重要提醒:Android 10 以后,后台定位权限收得很紧。如果你的 App 需要在后台使用 PASSIVE_PROVIDER,必须申请 ACCESS_BACKGROUND_LOCATION 权限。而且 Google Play 对后台定位的审核非常严格,不是随便就能过的。

好了,三种 Provider 的核心内容就这些。记住一句话:没有最好的 Provider,只有最合适的组合。根据你的业务场景,灵活搭配才是王道。


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