位置服务管理器(LocationManager)
首先,我们需要1个LocationManager,考虑到它会被多个方法使用,我们将它定义成Activity的Field。然后在onCreate方法中为它赋值。
//变量定义??
private?LocationManager?locationManager;??
//得到LocationManager??
locationManager?=?(LocationManager)?this??
????????.getSystemService(Context.LOCATION_SERVICE);??
开启位置服务的监听
有了LocationManager之后,我们就可以开始监听位置的变化了。我们使用LocationManager中的方法:
requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener)
来设置监听器。
首先,我们要注意到第1个参数,这个参数的值为2选1,分别是:LocationManager.NETWORK_PROVIDER和LocationManager.GPS_PROVIDER,前者用于移动网络中获取位置,精度较低但速度很快,后者使用GPS进行定位,精度很高但一般需要10-60秒时间才能开始第1次定位,如果是在室内则基本上无法定位。
这2种Provider本质上是互补的,在本教程中,我们会同时开启2个监听,但基于移动网络的监听只会执行一次就会被停止,而基于GPS的监听则会一直持续下去,直至用户自己停止监听。
代码片段如下:
首先,我们会声明1个监听器的内部类,这个类会同时用于2种模式的监听。还要声明1个变量,用于记录当前的位置。
private?class?MyLocationListner?implements?LocationListener{??
????@Override??
????public?void?onLocationChanged(Location?location)?{??
????????//?Called?when?a?new?location?is?found?by?the?location?provider.??
????????Log.v("GPSTEST",?"Got?New?Location?of?provider:"+location.getProvider());??
????????if(currentLocation!=null){??
????????????if(isBetterLocation(location,?currentLocation)){??
????????????????Log.v("GPSTEST",?"It's?a?better?location");??
????????????????currentLocation=location;??
????????????????showLocation(location);??
????????????}??
????????????else{??
????????????????Log.v("GPSTEST",?"Not?very?good!");??
????????????}??
????????}??
????????else{??
????????????Log.v("GPSTEST",?"It's?first?location");??
????????????currentLocation=location;??
????????????showLocation(location);??
????????}??
????????//移除基于LocationManager.NETWORK_PROVIDER的监听器??
????????if(LocationManager.NETWORK_PROVIDER.equals(location.getProvider())){??
????????????locationManager.removeUpdates(this);??
????????}??
????}??
????//后3个方法此处不做处理??
????public?void?onStatusChanged(String?provider,?int?status,?Bundle?extras)?{??
????}??
????public?void?onProviderEnabled(String?provider)?{??
????}??
????public?void?onProviderDisabled(String?provider)?{??
????}??
};??
Location?currentLocation;??
private?void?showLocation(Location?location){??
????//纬度??
????Log.v("GPSTEST","Latitude:"+location.getLatitude());??
????//经度??
????Log.v("GPSTEST","Longitude:+location.getLongitude());??
????//精确度??
????Log.v("GPSTEST","Accuracy:"+location.getAccuracy());??
????//Location还有其它属性,请自行探索??
}??
以下是开始监听
private?LocationListener?gpsListener=null;??
????private?LocationListener?networkListner=null;??
????private?void?registerLocationListener(){??
????????networkListner=new?MyLocationListner();??
????????locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,?3000,?0,?networkListner);??
????????gpsListener=new?MyLocationListner();??
????????locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,?5000,?0,?gpsListener);??
????}??
以上的代码还是很易懂的吧,创建1个监听器对象,然后指定1个provider,然后requestLocationUpdates。在监听器中检查如果是NETWORK_PROVIDER,则取消监听,只留GPS的监听(在你的实际应用中可以根据情况来进行,因为GPS有可能会因为建筑的阻挡而暂时不工作)。
位置精度的判断
在showLocation方法中,我们打印了location的Accuracy属性,这就是精确度,一般来说NETWORK得到的位置精度一般在500-1000米,GPS得到的精度一般在5-50米,基于这个属性我们可以对精度进行判断,以决定是否采用这个精度。
你可能已经注意到上面的代码中有1个isBetterLocation方法,这是用来判断获取的位置是否更好,事实上这个方法来自于Dev Guide。我们看下这个方法的内容:
private?static?final?int?CHECK_INTERVAL?=?1000?*?30;??
????protected?boolean?isBetterLocation(Location?location,??
????????????Location?currentBestLocation)?{??
????????if?(currentBestLocation?==?null)?{??
????????????//?A?new?location?is?always?better?than?no?location??
????????????return?true;??
????????}??
????????//?Check?whether?the?new?location?fix?is?newer?or?older??
????????long?timeDelta?=?location.getTime()?-?currentBestLocation.getTime();??
????????boolean?isSignificantlyNewer?=?timeDelta?>?CHECK_INTERVAL;??
????????boolean?isSignificantlyOlder?=?timeDelta?<?-CHECK_INTERVAL;??
????????boolean?isNewer?=?timeDelta?>?0;??
????????//?If?it's?been?more?than?two?minutes?since?the?current?location,??
????????//?use?the?new?location??
????????//?because?the?user?has?likely?moved??
????????if?(isSignificantlyNewer)?{??
????????????return?true;??
????????????//?If?the?new?location?is?more?than?two?minutes?older,?it?must??
????????????//?be?worse??
????????}?else?if?(isSignificantlyOlder)?{??
????????????return?false;??
????????}??
????????//?Check?whether?the?new?location?fix?is?more?or?less?accurate??
????????int?accuracyDelta?=?(int)?(location.getAccuracy()?-?currentBestLocation??
????????????????.getAccuracy());??
????????boolean?isLessAccurate?=?accuracyDelta?>?0;??
????????boolean?isMoreAccurate?=?accuracyDelta?<?0;??
????????boolean?isSignificantlyLessAccurate?=?accuracyDelta?>?200;??
????????//?Check?if?the?old?and?new?location?are?from?the?same?provider??
????????boolean?isFromSameProvider?=?isSameProvider(location.getProvider(),??
????????????????currentBestLocation.getProvider());??
????????//?Determine?location?quality?using?a?combination?of?timeliness?and??
????????//?accuracy??
????????if?(isMoreAccurate)?{??
????????????return?true;??
????????}?else?if?(isNewer?&&?!isLessAccurate)?{??
????????????return?true;??
????????}?else?if?(isNewer?&&?!isSignificantlyLessAccurate??
????????????????&&?isFromSameProvider)?{??
????????????return?true;??
????????}??
????????return?false;??
????}??
????/**?Checks?whether?two?providers?are?the?same?*/??
????private?boolean?isSameProvider(String?provider1,?String?provider2)?{??
????????if?(provider1?==?null)?{??
????????????return?provider2?==?null;??
????????}??
????????return?provider1.equals(provider2);??
????}?
从代码中可以很清楚的看出判断位置是否“更好”的准则,不仅使用了精度(getAccuracy()),还使用了时间进行判断。事实上除了在导航应用,其它的时候均可以直接使用上面的这个方法来对位置更新信息进行过滤,以减少不断更新界面而带来的性能损失。
结束监听
只需要调用LocationManager对象的removeUpdates(LocationListener listener)方法就可以停止监听。事实上,在之前的代码中你已经看到我们移除了基于移动网络的监听器,下面的代码片段用于移除GPS监听器。
if(gpsListener!=null){??
????????locationManager.removeUpdates(gpsListener);??
????????gpsListener=null;??
????}??
LocationManager的其它使用
这里还要介绍LocationManager中的几个方法:
getLastKnownLocation(String provider),用于得到上次定位时的最后位置,通常在应用刚启动时立刻得到1个位置,这样应用看上去会比较快。
getBestProvider(Criteria criteria, boolean enabledOnly),根据条件(精度的高低,是否能够得到海拔等)以及当前是否开启,得到1个最好的位置Provider。看上去很美,但现在的Android系统中只有2个Provider,而大多数用户的GPS都是开启的,在仅仅是2选1的情况我想像不出这个方法的用途。而即便用户关闭了GPS,我们也有能力帮他开启,用完了之后再关掉它。开启的方法见《进阶:如何编程实现开启或关闭GPS?》。
总结
2个Provider提供了不同精度的定位服务,我们可以根据情况来使用。
一般来说,先使用NETWORK来得到1个精度较差的位置,再使用GPS来得到更准确的位置。
在Android官方提供的Dev Guide中,提供了一个关于GPS使用的时间线,我们来看一下