|  
                         在之前的教程《进阶:如何编程实现开启或关闭GPS?》中,讲述了GPS开关的问题,包括了Android 
                          2.2以及之前版本的2种不同的开关方法。虽然我说过在官方的Dev Guide中有相当详细的Location定位的说明,不过我决定还是将这个教程补齐。 
                         在本教程中,我会分别讲述如何开启位置服务的监听,如何停止监听,如何获得不同精度的定位,以及如何判断定位是否更精确。 
                         Android中的定位服务的相关类基本上都在android.location包中,下面会按编写的顺序依次讲解。 
                         位置服务管理器(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使用的时间线,我们来看一下 
                          
                        概括起来就是2句话:“快速反应,渐进式精确”。在实际的使用中也要根据自己的情况画1个时间线,好决定何时开始监听,何时结束监听。  |