最近为了做LBS功能模块,到网上搜了一些资料,大多数介绍都是使用繁琐的基站定位,要自己去读取什么CellId,LocationAreaCode, MobileCountryCode,MobileNetworkCode等参数,而且多数是针对GSM/UMTS。而自己使用的CDMA,跟上面的参数叫法不一样,还得自己一个一个去对应。虽然最后算是解决了,但是难道就没有更好的办法吗。

翻了翻Android Developer找到一个不错的东西LocationManager。LocationManager是通过listener的方式来告知调用者,而原来写好的模块是直接return的,于是得稍微改造一下:

首先定义一个Model:

   
   
   
   
  1. public class LocationData {  
  2.         String lat;  
  3.         String lon;  
  4.         String address;  
  5. }  

 

然后LBS的所有功能都封装到一个工具类里面:

首先在构造函数里面获取系统服务中的LocationManager:

   
   
   
   
  1. public class LBSTool {  
  2.     private Context mContext;  
  3.     private LocationManager mLocationManager;   
  4.     private LocationData mLocation;  
  5.     private LBSThread mLBSThread;  
  6.     private MyLocationListner mNetworkListner;  
  7.     private MyLocationListner mGPSListener;  
  8.     private Looper mLooper;  
  9.       
  10.     public LBSTool(Context context) {  
  11.         mContext = context;  
  12.         //获取Location manager  
  13.         mLocationManager = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);  
  14.     }  
  15.   
  16. ......  
  17. }  

 

 然后是入口方法,这里会启动一个子线程去获取地理位置信息,并让主线程进入等待,时长通过timeout设置

   
   
   
   
  1. /**  
  2.  * 开始定位   
  3.  * @param timeout 超时设置  
  4.  * @return LocationData位置数据,如果超时则为null  
  5.  */  
  6. public LocationData getLocation(long timeout) {  
  7.     mLocation = null;  
  8.     mLBSThread = new LBSThread();  
  9.     mLBSThread.start();//启动LBSThread  
  10.     timeout = timeout > 0 ? timeout : 0;  
  11.       
  12.     synchronized (mLBSThread) {  
  13.         try {  
  14.             Log.i(Thread.currentThread().getName(), "Waiting for LocationThread to complete...");  
  15.             mLBSThread.wait(timeout);//主线程进入等待,等待时长timeout ms  
  16.             Log.i(Thread.currentThread().getName(), "Completed.Now back to main thread");  
  17.         }  
  18.         catch (InterruptedException e) {  
  19.             e.printStackTrace();  
  20.         }  
  21.     }  
  22.     mLBSThread = null;  
  23.     return mLocation;  

子线程通过调用registerLocationListener开启位置服务的监听,并且讲监听器分配给指定looper

   
   
   
   
  1. private class LBSThread extends Thread {  
  2.     @Override  
  3.     public void run() {  
  4.         setName("location thread");   
  5.         Log.i(Thread.currentThread().getName(), "--start--");   
  6.         Looper.prepare();//给LBSThread加上Looper  
  7.         mLooper = Looper.myLooper();  
  8.         registerLocationListener();  
  9.         Looper.loop();  
  10.         Log.e(Thread.currentThread().getName(),  "--end--");  
  11.           
  12.     }  
  13. }  
  14.   
  15. private void registerLocationListener () {  
  16.     Log.i(Thread.currentThread().getName(), "registerLocationListener");          
  17.     if (isGPSEnabled()) {  
  18.         mGPSListener=new MyLocationListner();    
  19.   
  20.         //五个参数分别为位置服务的提供者,最短通知时间间隔,最小位置变化,listener,listener所在消息队列的looper  
  21.         mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 50000, mGPSListener, mLooper);    
  22.     }  
  23.     if (isNetworkEnabled()) {  
  24.         mNetworkListner=new MyLocationListner();    
  25.  
  26.         mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 30000, mNetworkListner, mLooper);    
  27.     }  

 isGPSEnabled和isNetworkEnabled分别为判断当前手机是否开启了GPS以及网络的状况(包含了是否开启wifi和移动网络),以决定使用哪一种服务提供者:GPS_PROVIDER或者NETWORK_PROVIDER。

   
   
   
   
  1. /**  
  2.  * 判断GPS是否开启  
  3.  * @return  
  4.  */  
  5. public boolean isGPSEnabled() {  
  6.     if(mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {  
  7.         Log.i(Thread.currentThread().getName(), "isGPSEnabled");  
  8.         return true;  
  9.     }   
  10.     else {  
  11.         return false;  
  12.     }  
  13. }  
  14.   
  15. /**  
  16.  * 判断Network是否开启(包括移动网络和wifi)  
  17.  * @return  
  18.  */  
  19. public boolean isNetworkEnabled() {  
  20.     return (isWIFIEnabled() || isTelephonyEnabled());   
  21. }  
  22.   
  23. /**  
  24.  * 判断移动网络是否开启  
  25.  * @return  
  26.  */  
  27. public boolean isTelephonyEnabled() {  
  28.     boolean enable = false;  
  29.     TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);  
  30.     if (telephonyManager != null) {  
  31.         if (telephonyManager.getNetworkType() != TelephonyManager.NETWORK_TYPE_UNKNOWN) {  
  32.             enable = true;  
  33.             Log.i(Thread.currentThread().getName(), "isTelephonyEnabled");  
  34.         }  
  35.     }  
  36.       
  37.     return enable;  
  38. }  
  39.   
  40. /**  
  41.  * 判断wifi是否开启  
  42.  */  
  43. public boolean isWIFIEnabled() {  
  44.     boolean enable = false;  
  45.     WifiManager wifiManager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);  
  46.     if(wifiManager.isWifiEnabled()) {  
  47.         enable = true;  
  48.         Log.i(Thread.currentThread().getName(), "isWIFIEnabled");   
  49.     }   
  50.     return enable;  

当LocationManager在大于最短时间且检测到最小位置变化时,就会通知给监听器,然后我们就可以通过返回的经纬度信息去google服务器查找对应的地址,然后停止LocationManger的工作,解除LBSThread中的Looper,让LBSThread结束,最后通知主线程可以继续,整个流程结束。

   
   
   
   
  1. private class MyLocationListner implements LocationListener{    
  2.   
  3.     @Override  
  4.     public void onLocationChanged(Location location) {    
  5.         // 当LocationManager检测到最小位置变化时,就会回调到这里  
  6.         Log.i(Thread.currentThread().getName(), "Got New Location of provider:"+location.getProvider());  
  7.         unRegisterLocationListener();//停止LocationManager的工作  
  8.         try {  
  9.             synchronized (mLBSThread) {   
  10.                 parseLatLon(location.getLatitude()+"", location.getLongitude()+"");//解析地理位置  
  11.                 mLooper.quit();//解除LBSThread的Looper,LBSThread结束  
  12.                 mLBSThread.notify();//通知主线程继续  
  13.             }  
  14.         }  
  15.         catch (Exception e) {  
  16.             e.printStackTrace();  
  17.         }  
  18.     }    
  19.  
  20.     //后3个方法此处不做处理 
  21.     @Override  
  22.     public void onStatusChanged(String provider, int status, Bundle extras) {}    
  23.  
  24.     @Override  
  25.     public void onProviderEnabled(String provider) {}    
  26.  
  27.     @Override  
  28.     public void onProviderDisabled(String provider) {}  
  29.   
  30. };  
  31.   
  32. /**  
  33.  * 使用经纬度从goole服务器获取对应地址   
  34.  * @param 经纬度  
  35.  */  
  36. private void parseLatLon(String lat, String lon) throws Exception {  
  37.     Log.e(Thread.currentThread().getName(),  "---parseLatLon---");  
  38.     Log.e(Thread.currentThread().getName(),  "---"+lat+"---");  
  39.     try {  
  40.         HttpClient httpClient = new DefaultHttpClient();  
  41.         HttpGet get = new HttpGet("http://ditu.google.cn/maps/geo?output=json&q="+lat+","+lon);  
  42.         HttpResponse response = httpClient.execute(get);  
  43.         String resultString = EntityUtils.toString(response.getEntity());  
  44.           
  45.         JSONObject jsonresult = new JSONObject(resultString);  
  46.         if(jsonresult.optJSONArray("Placemark") != null) {  
  47.             mLocation = new LocationData();  
  48.             mLocation.lat = lat;  
  49.             mLocation.lon = lon;  
  50.             mLocation.address = jsonresult.optJSONArray("Placemark").optJSONObject(0).optString("address");  
  51.         }  
  52.     }  
  53.     catch (Exception e) {  
  54.         e.printStackTrace();  
  55.     }  
  56. }  
  57.   
  58. /**  
  59.  * 注销监听器   
  60.  */  
  61. private void unRegisterLocationListener () {  
  62.     if(mGPSListener!=null){    
  63.         mLocationManager.removeUpdates(mGPSListener);    
  64.         mGPSListener=null;    
  65.     }   
  66.     if(mNetworkListner!=null){    
  67.         mLocationManager.removeUpdates(mNetworkListner);    
  68.         mNetworkListner=null;    
  69.     }   

 

接下来可以在界面上安放个button:

   
   
   
   
  1. locationBtn.setOnClickListener(new OnClickListener() {  
  2.               
  3.     @Override  
  4.     public void onClick(View v) {  
  5.         //return mode  
  6.         LBSTool lbs = new LBSTool(LBStestActivity.this);  
  7.         LocationData location = lbs.getLocation(120000);  
  8.         if (location != null) {  
  9.             Log.i("---lat---",location.lat);  
  10.             Log.i("---lon---",location.lon);  
  11.             Log.i("---address---",location.address);  
  12.             Toast.makeText(LBStestActivity.this, location.lat + " " + location.lon + " " + location.address, Toast.LENGTH_LONG).show();  
  13.  
  14.         }  
  15.           
  16.     }  
  17. }); 

最后别忘了加入权限:

   
   
   
   
  1. <uses-permission android:name="android.permission.INTERNET" />  
  2. <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
  3. <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>  
  4. <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />  
  5. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />  

 

此外,LocationManager还有些高级的用法,比如设置一些关键参数,以及获取最后一次定位信息等等:

   
   
   
   
  1. Criteria criteria = new Criteria(); 
  2.         criteria.setAccuracy(Criteria.ACCURACY_FINE); // 高精度 
  3.         criteria.setAltitudeRequired(false); 
  4.         criteria.setBearingRequired(false); 
  5.         criteria.setCostAllowed(true); 
  6.         criteria.setPowerRequirement(Criteria.POWER_LOW); // 低功耗 
  7.          
  8.         String bestprovider = locationManager.getBestProvider(criteria, true); // 获取GPS信息 
  9.         Location location = locationManager.getLastKnownLocation(bestprovider); // 通过GPS获取位置 
  10.         Log.e("--bestprovider--", bestprovider); 
  11.         Log.e("--bestprovider--", location.getLatitude()+"");