解析 locationManager.requestLocationUpdates() 方法
这段代码是Android中用于请求位置更新的方法调用。我来详细解释它的含义和可调整参数:
方法解释
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, // 位置提供者
0, // 最小时间间隔(毫秒)
0, // 最小距离间隔(米)
locationListener // 位置监听器
);
这个方法告诉系统你想要定期接收位置更新,当位置发生变化时会通过locationListener回调通知你。
参数详解及可调整变量
-
位置提供者 (provider):
-
LocationManager.GPS_PROVIDER- 使用GPS卫星定位,精度高但耗电,需要户外 -
LocationManager.NETWORK_PROVIDER- 使用网络定位(WiFi/基站),精度较低但室内可用 -
LocationManager.PASSIVE_PROVIDER- 被动接收其他应用请求的位置更新
-
-
最小时间间隔 (minTime, 毫秒):
- 这里设置为0表示尽可能快地获取更新
- 可以调整为更大的值以减少耗电,例如5000表示至少5秒更新一次
- 实际更新时间可能比这个长,但不会比它短
-
最小距离间隔 (minDistance, 米):
- 这里设置为0表示位置有任何变化都通知
- 可以设置为例如10,表示移动超过10米才通知
- 有助于减少不必要的回调
-
位置监听器 (listener):
- 需要实现
LocationListener接口 - 接收四个回调方法:
-
onLocationChanged(Location location)- 位置更新时调用 onStatusChanged(String provider, int status, Bundle extras)onProviderEnabled(String provider)onProviderDisabled(String provider)
-
- 需要实现
使用建议
-
为了平衡精度和电池消耗,可以考虑:
// 每5秒或移动10米以上时更新 locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 5000, 10, locationListener); -
在Android 6.0+上需要先请求位置权限:
-
ACCESS_FINE_LOCATION(GPS) -
ACCESS_COARSE_LOCATION(网络)
-
-
不再需要更新时,记得调用:
locationManager.removeUpdates(locationListener); 对于现代Android开发,推荐使用Fused Location Provider (Google Play Services)代替直接使用LocationManager,它能更智能地管理电量和精度平衡。
位置提供者 (provider)可以混合使用吗
位置提供者(provider)可以混合使用吗?
在Android定位系统中,位置提供者是可以混合使用的,但需要注意一些实现细节和最佳实践。
混合使用位置提供者的方式
-
同时注册多个提供者:
你可以为同一个LocationListener注册多个提供者:locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, minTime, minDistance, listener); locationManager.requestUpdates(LocationManager.NETWORK_PROVIDER, minTime, minDistance, listener); -
不同监听器处理不同提供者:
// GPS监听器 locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, minTime, minDistance, gpsListener); // 网络监听器 locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, minTime, minDistance, networkListener);
混合使用的注意事项
-
回调顺序不确定:
- 你无法预知哪个提供者会先返回位置
- 同一个监听器可能收到来自不同提供者的回调
-
精度处理:
- GPS通常比网络定位更精确
- 可以在
onLocationChanged()中比较位置精度,选择使用更精确的结果
-
电量消耗:
- 同时使用多个提供者会增加电量消耗
- 特别是GPS和网络同时激活时
-
现代替代方案:
- 推荐使用
FusedLocationProviderClient(Google Play Services的一部分) - 它会自动选择最佳提供者并优化电量使用
- 推荐使用
示例代码:混合使用并选择最佳位置
LocationListener locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
// 比较新位置和之前保存的位置,选择更精确的
if (bestLocation == null || location.getAccuracy() < bestLocation.getAccuracy()) {
bestLocation = location;
// 使用最佳位置更新UI或进行其他操作
}
}
// ...其他回调方法
};
// 注册两个提供者
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 10, locationListener);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000, 50, locationListener);
最佳实践建议
-
根据场景选择:
- 户外导航:优先使用GPS
- 室内定位:使用网络定位
- 快速获取初始位置:先网络后GPS
-
超时切换机制:
- 如果GPS长时间无法定位,可以切换到网络定位
-
权限管理:
- 同时需要
ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION权限
- 同时需要
-
电量优化:
- 获取到足够精确的位置后,减少更新频率或停止不必要的提供者
对于大多数现代应用,Google推荐的Fused Location Provider已经内置了这些优化策略,通常是更好的选择。
返回的位置是什么坐标系
Android 定位返回的坐标系
Android 定位系统返回的位置坐标使用的是 WGS84 坐标系(World Geodetic System 1984),这是全球通用的 GPS 标准坐标系。
坐标系详情
-
WGS84 坐标系特点:
- 经度 (Longitude):-180 到 180 度,东经为正,西经为负
- 纬度 (Latitude):-90 到 90 度,北纬为正,南纬为负
- 海拔高度 (Altitude):相对于 WGS84 椭球面的高度,单位米
-
Location 对象中的坐标数据:
Location location; // 从onLocationChanged获取的位置对象 double latitude = location.getLatitude(); // 纬度 double longitude = location.getLongitude(); // 经度 double altitude = location.getAltitude(); // 海拔(可能有值也可能为0) float accuracy = location.getAccuracy(); // 水平精度半径(米)
其他坐标系转换
如果需要使用其他坐标系(如中国常用的GCJ-02或BD-09),需要自行转换:
-
中国特有坐标系:
- GCJ-02(火星坐标系):中国官方对WGS84加密后的坐标系
- BD-09:百度在GCJ-02基础上二次加密的坐标系
-
转换方法:
// WGS84转GCJ-02示例 public static native double[] wgs84ToGcj02(double wgsLat, double wgsLon); // GCJ-02转BD-09示例 public static native double[] gcj02ToBd09(double gcjLat, double gcjLon);(实际实现需要引入相应的算法库或使用第三方SDK)
谷歌地图是WGS-84坐标系,但在国内是使用GCJ-02坐标系。
百度地图是在使用GCJ-02 加密的基础上,又通过自己的加密算法BD-09 又对坐标进行了二次加密。
我国要求,每一个地图产品都要使用“ GCJ-02 ” 进行首次加密,不能直接使用原始坐标体系。高德、腾讯等都是用的这个坐标系。
重要注意事项
-
中国大陆地区特殊要求:
- 在中国大陆,所有公开地图API返回的坐标都必须是GCJ-02坐标系
- 直接使用原始GPS坐标(WGS84)在地图上会有偏移
-
精度信息:
-
location.getAccuracy()返回的是以米为单位的水平精度半径 - 值越小表示精度越高(GPS通常1-20米,网络定位通常20-500米)
-
-
时间戳:
-
location.getTime()返回UTC时间戳(毫秒),可通过new Date(location.getTime())转换
-
-
速度信息(如果有):
-
location.getSpeed()米/秒 -
location.getBearing()方向角度(相对于正北)
-
如果您需要在中国地图上显示位置,必须将WGS84坐标转换为GCJ-02或BD-09坐标系,否则会出现位置偏移问题。
在安卓开发中(java),如何将WGS84坐标转换为BD-09坐标系?
在Android Java中将WGS84坐标转换为BD-09坐标系
在中国地图应用开发中,由于坐标加密系统的存在,需要将GPS获取的WGS84坐标先转换为GCJ-02(火星坐标),再转换为BD-09(百度坐标)才能正确显示在百度地图上。
完整转换实现
以下是一个完整的Java实现,包含WGS84→GCJ-02和GCJ-02→BD-09的转换:
public class CoordinateConverter {
private static final double x_PI = 3.14159265358979324 * 3000.0 / 180.0;
private static final double PI = 3.1415926535897932384626;
private static final double a = 6378245.0;
private static final double ee = 0.00669342162296594323;
/**
* WGS84转GCJ02(火星坐标系)
*/
public static double[] wgs84ToGcj02(double lng, double lat) {
if (outOfChina(lng, lat)) {
return new double[]{lng, lat};
}
double dlat = transformLat(lng - 105.0, lat - 35.0);
double dlng = transformLng(lng - 105.0, lat - 35.0);
double radlat = lat / 180.0 * PI;
double magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
double sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
double mglat = lat + dlat;
double mglng = lng + dlng;
return new double[]{mglng, mglat};
}
/**
* GCJ02转BD09(百度坐标系)
*/
public static double[] gcj02ToBd09(double lng, double lat) {
double z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI);
double theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI);
double bd_lng = z * Math.cos(theta) + 0.0065;
double bd_lat = z * Math.sin(theta) + 0.006;
return new double[]{bd_lng, bd_lat};
}
/**
* WGS84转BD09(百度坐标系) - 两步转换
*/
public static double[] wgs84ToBd09(double lng, double lat) {
double[] gcj02 = wgs84ToGcj02(lng, lat);
return gcj02ToBd09(gcj02[0], gcj02[1]);
}
private static boolean outOfChina(double lng, double lat) {
return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55);
}
private static double transformLat(double lng, double lat) {
double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
return ret;
}
private static double transformLng(double lng, double lat) {
double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;
return ret;
}
}
使用示例
// 从Location获取WGS84坐标
Location location = ...; // 从GPS获取的位置
double wgsLng = location.getLongitude();
double wgsLat = location.getLatitude();
// 转换为BD-09坐标系
double[] bd09 = CoordinateConverter.wgs84ToBd09(wgsLng, wgsLat);
double bdLng = bd09[0];
double bdLat = bd09[1];
Log.d("Coordinate", "原始坐标(WGS84): " + wgsLng + "," + wgsLat);
Log.d("Coordinate", "百度坐标(BD09): " + bdLng + "," + bdLat);
注意事项
-
中国大陆地区才需要转换:
outOfChina方法会判断坐标是否在中国境内,境外坐标不需要转换 - 精度问题:转换算法有一定精度误差,通常在实际应用中可忽略
-
百度地图SDK:如果使用百度地图SDK,它提供了内置的坐标转换方法
// 使用百度地图SDK的转换方法 LatLng sourceLatLng = new LatLng(wgsLat, wgsLng); LatLng desLatLng = CoordinateConverter.convert(sourceLatLng, CoordType.GPS); // GPS即WGS84 - 性能考虑:频繁转换可能影响性能,建议在需要显示时再转换
其他方案
-
使用百度地图Web API:
// 调用百度地图坐标转换API(需要网络) String url = "http://api.map.baidu.com/geoconv/v1/?coords=" + lng + "," + lat + "&from=1&to=5&ak=您的AK"; 使用第三方库:如
coordtransform等开源库
以上实现可以满足大多数Android应用中WGS84到BD-09坐标转换的需求。