14、位置传感器实战:GPS与网络定位、LocationManager使用、地理围栏实现、位置更新策略
各位同学,今天我们来聊聊位置传感器。说实话,位置服务是Android开发里最常用、也最容易踩坑的功能之一。我自己做过的项目里,十个有八个都跟位置有关——外卖、打车、运动记录,哪个离得开定位?
这一章,我会带大家把位置服务的几个核心点捋清楚:GPS和网络定位怎么选、LocationManager怎么用、地理围栏怎么实现、位置更新策略怎么定。嗯,咱们一个一个来。
14.1 GPS与网络定位:谁更靠谱?
先说说定位方式。Android里主要有两种:GPS定位和网络定位。
GPS定位,说白了就是靠卫星。精度高,能到3-5米,但缺点也明显——室内基本废了,而且首次定位慢,冷启动可能要一两分钟。我在做户外运动App时,就遇到过用户抱怨“打开App半天定不到位”,其实就是GPS冷启动的问题。
网络定位,靠的是基站和Wi-Fi。速度快,室内也能用,但精度嘛...几十米到几百米不等。适合那些不需要精确位置、但需要快速响应的场景,比如天气App、新闻推送。
那怎么选?我个人习惯是:能混用就别单选。Android提供了Criteria类,可以设置精度、功耗等要求,让系统帮你选最合适的Provider。
核心原则:户外高精度用GPS,室内快速响应用网络,两者结合最稳妥。
14.2 LocationManager使用:从入门到实战
LocationManager是Android位置服务的核心API。别看它老,但稳定可靠。我到现在还在用,没出过什么大问题。
先看一个基本的使用流程:
// 获取LocationManager
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// 检查GPS是否开启
boolean isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
// 请求位置更新
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, // 使用GPS
5000, // 最小时间间隔:5秒
10, // 最小距离变化:10米
locationListener // 回调监听
);
这里有个坑,我曾经踩过:别忘了在Android 6.0以上动态申请权限。位置权限分两种:ACCESS_FINE_LOCATION(精确定位)和ACCESS_COARSE_LOCATION(粗略定位)。如果你只用网络定位,申请粗略权限就够了;如果用GPS,必须申请精确定位权限。
注意:从Android 10开始,后台位置权限需要单独申请,而且Google Play审核非常严格。如果你的App不需要后台定位,千万别申请,否则大概率被拒。
再说说LocationListener。它有几个回调方法,最常用的是onLocationChanged()。但要注意,这个方法在Android 12以后被标记为废弃了,推荐用LocationCallback替代。不过老项目里还是经常见到,大家知道就行。
14.3 地理围栏实现:让App感知“边界”
地理围栏,说白了就是画个圈,用户进圈或出圈时触发事件。比如你走到商场附近,App自动推送优惠券——这就是地理围栏的典型应用。
Android提供了GeofencingClient来实现。我给大家画个流程图,看看整体逻辑:
看明白了吗?流程不复杂,但实现细节要注意。我给大家写个核心代码:
// 1. 定义围栏
Geofence geofence = new Geofence.Builder()
.setRequestId("fence_001")
.setCircularRegion(39.9042, 116.4074, 100) // 北京天安门,半径100米
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
Geofence.GEOFENCE_TRANSITION_EXIT)
.build();
// 2. 创建请求
GeofencingRequest request = new GeofencingRequest.Builder()
.addGeofence(geofence)
.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
.build();
// 3. 注册
GeofencingClient geofencingClient = LocationServices.getGeofencingClient(this);
geofencingClient.addGeofences(request, getGeofencePendingIntent())
.addOnSuccessListener(aVoid -> {
// 注册成功
})
.addOnFailureListener(e -> {
// 注册失败,检查权限和Google Play服务
});
小技巧:地理围栏的半径不要设太小,建议至少100米。我见过有人设10米,结果用户走半天触发不了——因为GPS精度本身就有波动,太小的围栏根本检测不到。
14.4 位置更新策略:省电与精度的平衡
位置更新策略,说白了就是怎么在省电和精度之间找平衡。这个问题我纠结了很久,后来总结出一套经验。
先看几个关键参数:
| 参数 | 说明 | 建议值 |
|---|---|---|
| minTime | 最小时间间隔(毫秒) | 运动类:1000-3000;普通:5000-10000 |
| minDistance | 最小距离变化(米) | 运动类:5-10;普通:20-50 |
| priority | 优先级(高精度/平衡/低功耗) | 按场景选择,不要一味追求高精度 |
我个人习惯的做法是:动态调整策略。比如用户静止时,降低更新频率;用户移动时,提高频率。怎么判断用户是否移动?用ActivityRecognitionClient检测用户状态,然后动态调整LocationRequest的参数。
// 动态调整示例
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setInterval(10000); // 默认10秒
locationRequest.setFastestInterval(5000);
locationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
// 当检测到用户正在步行时,提高精度
if (detectedActivity == DetectedActivity.WALKING) {
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(3000);
}
避坑指南:我曾经在一个运动App里,把minTime设成了1000毫秒,结果用户手机半小时就没电了。后来改成动态策略,续航提升了3倍。记住:能省则省,别让用户骂你“电老虎”。
还有一个容易被忽略的点:位置更新的生命周期管理。在Activity或Fragment的onPause()里一定要移除更新,在onResume()里重新请求。否则用户切到后台,你的App还在疯狂定位,等着被系统杀进程吧。
@Override
protected void onPause() {
super.onPause();
locationManager.removeUpdates(locationListener);
}
@Override
protected void onResume() {
super.onResume();
if (checkLocationPermission()) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
5000, 10, locationListener
);
}
}
好了,这一章的内容就到这里。位置服务看似简单,但要做好真不容易。从GPS和网络定位的选择,到LocationManager的使用,再到地理围栏和更新策略,每一步都有讲究。希望大家在实际项目中,能把这些知识点用起来,少踩坑、多省电。
公众号:蓝海资料掘金营,微信deep3321