先抛问题:
- locationManager.getLastKnownLocation方法返回null。
- 如何实现快速而又精确的定位。
手机定位方案
我们常用的定位一般也就下面这两种发方案:
- GPS定位
- 网络定位(基站定位)
最简单的手机定位方式当然是通过GPS模块(现在大部分的智能机应该都有了)。GPS方式准确度是最高的,但是它的缺点也非常明显:
- 比较耗电;
- 绝大部分用户默认不开启GPS模块;
- 从GPS模块启动到获取第一次定位数据,可能需要比较长的时间;
- 室内几乎无法使用。
这其中,缺点2,3都是比较致命的。需要指出的是,GPS走的是卫星通信的通道,在没有网络连接的情况下也能用。
另外一种常见的定位方式也就是网络定位了,也就基站定位。大致思路就是采集到手机上的基站ID号(cellid)和其它的一些信息(MNC,MCC,LAC等等),然后通过网络访问一些定位服务,获取并返回对应的经纬度坐标。基站定位的精确度不如GPS,但好处是能够在室内用,只要网络通畅就行。
还有Wifi定位。和基站定位类似,这种方式是通过获取当前所用的wifi的一些信息,然后访问网络上的定位服务以获得经纬度坐标。因为它和基站定位其实都需要使用网络,所以在Android也统称为Network方式。另外,需要指出的是网上相当一部分人说由于国内没有Google服务,无法实现网络定位,这个不敢苟同,用手机基站和WIFI节点的地址来大致定位位置,这种定位方式取决于服务器,即取决于将基站或WIF节点信息翻译成位置信息的服务器的能力。市面上的手机厂商基本在手机系统底部是已经接入国内位置信息服务器的,所以说在国内无法实现网络定位的说法是不存在的。
不多说,来看看一般的实现定位的代码:
LocationManager mLocationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);//高精度
criteria.setAltitudeRequired(false);//无海拔要求 criteria.setBearingRequired(false);//无方位要求
criteria.setCostAllowed(true);//允许产生资费 criteria.setPowerRequirement(Criteria.POWER_LOW);//低功耗
// 获取最佳服务对象
String provider = locationManager.getBestProvider(criteria,true);
locationManager.getLastKnownLocation(provider);
locationManager.getBestProvider(criteria,true);方法看起来很完美,但其实返回值就network、gps二选一。而且如果你要求高精度,它会优先检查GPS,如果手机开启了GPS就返回GPS,否则返回network。如果都没开启则返回null。
最要说的就是getLastKnownLocation()
方法,首先它获取的是最近一次的定位信息,如果第一次运行呢,当然为空,即使不为空,通过它拿到的位置也未必是实时的位置信息吧。网络定位耗时一般在2秒左右(网络差,时间会更长), 这些都是造成getLastKnownLocation为空的原因,而且如果采取的定位方式是gps,那么90%得到的Location会为空。
那么如果实现尽可能的精准定位呢?
谷歌是有为我们推荐的,可参看:https://developer.android.com/guide/topics/location/strategies.html
思路大概就是通过网络定位和gps定位的结合,来尽可能的获取准确定位,接下来,所要讨论是基于只定位一次的情况下,大部分情况下也确实是这样!
- 开启network和gps监听
- 获得network定位信息location后,移除network监听
- 获得gps定位信息location,移除gps监听
- 比较当前location和新获取的location哪个更好(来自gps)
既然结合两者,就要同时为两者添加监听 :
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME, MIN_DISTANCE, gpsLocationListener);
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIME, MIN_DISTANCE, networkListener);
其中MIN_TIME和MIN_DISTANCE根据自己实际情况定义。
网络定位监听器:
LocationListener networkListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
if (isBetterLocation(location, mLocation)) {
mLocation = location;
}
if (mLocation != null) {
mLocationManager.removeUpdates(this);
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
};
gps定位监听器
LocationListener gpsLocationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
if (isBetterLocation(location, mLocation)) {
mLocationManager.removeUpdates(networkListener);
mLocation = location;
}
if (mLocation != null) {
mLocationManager.removeUpdates(this);
}
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
isBetterLocation(Location location, Location currentBestLocation)
方法是参考的Google官方提供的,可自行查看.
最后不要忘了要清除监听
public void removeListener() {
if (mLocationManager != null) {
uniqueInstance = null;
mLocationManager.removeUpdates(networkListener);
mLocationManager.removeUpdates(gpsLocationListener);
}
}