现在的位置: 首页 > 综合 > 正文

Using a LocationListener

2017年12月24日 ⁄ 综合 ⁄ 共 11873字 ⁄ 字号 评论关闭

How do you determine whether a certain provider is enabled? And if not enabled, how do you prompt the user to enable that provider because your application may require it?

A LocationListener is simple in theory. It’s a trivial class that has callback methods that specify when a location has changed, when a provider status has changed, or when that provider has been enabled or disabled.

The first thing we’ll touch on is a simple check whether the GPS provider is enabled, and if it isn’t, prompting the user to enable it via the Android settings application. This is done in the onResume method with an AlertDialog and an Intent.

public class GetLocationWithGPS extends Activity {
	  public static final String LOC_DATA = "LOC_DATA";
	  private LocationManager locationMgr;
	  private  GpsListener gpsListener;
	  private GpsStatus gpsStatus;
	  private Handler handler;
	  private TextView title;
	  private TextView detail;
	  private TextView gpsEvents;
	  private TextView satelliteStatus;
	  
	  protected void onCreate(Bundle savedInstanceState){
		  super.onCreate(savedInstanceState);
		  setContentView(R.layout.get_location);
		  
		  title=(TextView)findViewById(R.id.title);
		  detail=(TextView)findViewById(R.id.detail);
		  gpsEvents=(TextView)findViewById(R.id.gps_events);
		  satelliteStatus=(TextView)findViewById(R.id.satellite_status);
		  
		  title.setText("Get current location via GPS");
		  detail.setText("Attempting to get current location...\n     (may take a few seconds)");
		  locationMgr=(LocationManager)getSystemService(Context.LOCATION_SERVICE);
		  gpsListener=new GpsListener();
		  //setup handler
		  handler =new Handler(){
			  public void handleMessage(Message m){
				  Log.d(Main.LOG_TAG, "Handler returned with message: " + m.toString());
				  //handle location found
				  if(m.what==LocationHelper.MESSAGE_CODE_LOCATION_FOUND){
					  detail.setText("HANDLER RETURNED\nlat:" + m.arg1 + "\nlon:" + m.arg2);
				  //handle location not found
				  }else if(m.what==LocationHelper.MESSAGE_CODE_LOCATION_NULL){
					  detail.setText("HANDLER RETURNED\nunable to get location");
				  //handle provider not present
				  }else if(m.what==LocationHelper.MESSAGE_CODE_PROVIDER_NOT_PRESENT){
					  detail.setText("HANDLER RETURNED\nprovider not present");
				  }
			  }
		  };
	  }
	  
	  protected void onResume(){
		  super.onResume();
		  
		  //is provider enabled
		  if(!locationMgr.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER)){
			 //use alertdialog if not
			  AlertDialog.Builder builder=new AlertDialog.Builder(this);
			  builder.setTitle("GPS is not enabled")
			  		 .setMessage("Would you like to go the location settings and enable GPS?")
			  		 .setCancelable(true)
			  		 .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
							// TODO Auto-generated method stub
							//take user to settings
							startActivity(new Intent(Settings.ACTION_SECURITY_SETTINGS));
						}
					})
					.setNegativeButton("No", new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
							// TODO Auto-generated method stub
							dialog.cancel();
							finish();
						}
					});
			  AlertDialog alert=builder.create();
			  alert.show();
		  }
		  else{
			  //set up locationhelper for later
			  LocationHelper locationHelper=new LocationHelper(locationMgr,handler,Main.LOG_TAG);
			// here we aren't using a progressdialog around getCurrentLocation (don't want to block entire UI)
		    // (but be advised that you could if the situation absolutely required it)
			  locationHelper.getCurrentLocation(30);
		  }
		  locationMgr.addGpsStatusListener(gpsListener);
	  }
	  
	  protected void onPause(){
		  super.onPause();
		  locationMgr.removeGpsStatusListener(gpsListener);
	  }
	  
	  private class GpsListener implements GpsStatus.Listener{
		@Override
		public void onGpsStatusChanged(int event) {
			// TODO Auto-generated method stub
			Log.d("GpsListener", "Status changed to " + event);
			switch (event) {
            case GpsStatus.GPS_EVENT_STARTED:
               gpsEvents.setText("GPS_EVENT_STARTED");
               break;
            case GpsStatus.GPS_EVENT_STOPPED:
               gpsEvents.setText("GPS_EVENT_STOPPED");
               break;
            // GPS_EVENT_SATELLITE_STATUS will be called frequently
            // all satellites in use will invoke it, don't rely on it alone
            case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
               // this is *very* chatty, only very advanced use cases should need this (avoid it if you don't need it)
               gpsStatus = locationMgr.getGpsStatus(gpsStatus);
               StringBuilder sb = new StringBuilder();
               for (GpsSatellite sat : gpsStatus.getSatellites()) {
                  sb.append("Satellite N:" + sat.getPrn() + " AZ:" + sat.getAzimuth() + " EL:" + sat.getElevation()
                           + " S/N:" + sat.getSnr() + "\n");
               }
               satelliteStatus.setText(sb.toString());
               break;
            case GpsStatus.GPS_EVENT_FIRST_FIX:
               gpsEvents.setText("GPS_EVENT_FIRST_FIX");
               break;
         }
		}
		  
	  }
}

Checking whether a certain location provider is enabled is a straightforward task. Also, when a provider isn’t enabled and your application requires it, you should fail
fast. You should let the user know that you can’t continue without the provider, and offer them the chance to enable it. If they don’t enable said provider, you can then
pare down the functionality of your application or simply quit.

If the provider in question is enabled, the next step is to utilize it to get data or perform location updates. Use LocationListener to get location updates.

If the provider in question is enabled, the next step is to utilize it to get data or perform location updates. Use LocationListener to get location updates.

Location providers return location information to interested applications through the use of registered observers with the LocationListener class. For our purposes, we’ve
placed the details of using a LocationListener in a LocationHelper class. We’ve done this to encapsulate some of the details of getting the current location and to make reuse easier.

We’ll set up and tear down the listener, and we’ll let it communicate back with our Activity using a Handler. Setting up the handler and getting ready to use the helper takes us to the onCreate method。

The Handler class is used to send and process Message (and Runnable) objects on a thread’s MessageQueue. We initially learned about Handler in chapter 5. We’ll be
using it to allow our helper to pass data back to our Activity.

/**
 * Helper class to encapsulate some of the common code needed to determine the
 * current location using FINE (GPS) provider. 
 * <p/>
 * If the most recent location is available for the FINE provider, and it is relatively
 * recent (within FIX_RECENT_BUFFER_TIME -- currently 30 seconds), it is returned back to
 * the caller using a Message indicating the results. 
 * <p/>
 * IF the most recent location is either not available, or too old to be used, the
 * a LocationListener is kicked off for a specified duration. Once the LocationListener 
 * either gets a good Location update, or the time is elapsed, a Message is sent back 
 * to the caller indicating the results.
 * <p/>
 * Example usage from an Activity:
 * <p/>
 * <pre>
 *     
 *     Handler handler = new Handler() {
 *        public void handleMessage(Message m) {
 *           Log.d(LOG_TAG, "Handler returned with message: " + m.toString());
 *           if (m.what == LocationHelper.MESSAGE_CODE_LOCATION_FOUND) {
 *              Toast.makeText(Activity.this, "HANDLER RETURNED -- lat:" + m.arg1 + " lon:" + m.arg2, Toast.LENGTH_SHORT)
 *                       .show();
 *           } else if (m.what == LocationHelper.MESSAGE_CODE_LOCATION_NULL) {
 *              Toast.makeText(Activity.this, "HANDLER RETURNED -- unable to get location", Toast.LENGTH_SHORT).show();
 *           } else if (m.what == LocationHelper.MESSAGE_CODE_PROVIDER_NOT_PRESENT) {
 *              Toast.makeText(Activity.this, "HANDLER RETURNED -- provider not present", Toast.LENGTH_SHORT).show();
 *           }
 *        }
 *     };
 *     
 *     LocationHelper helper = new LocationHelper(locationManager, handler, LOG_TAG);
 *     helper.getCurrentLocation(handler); 
 * </pre> 
 * 
 * @author ccollins
 */
public class LocationHelper {
	   public static final int MESSAGE_CODE_LOCATION_FOUND = 1;
	   public static final int MESSAGE_CODE_LOCATION_NULL = 2;
	   public static final int MESSAGE_CODE_PROVIDER_NOT_PRESENT = 3;
	   private static final int FIX_RECENT_BUFFER_TIME = 30000;
	   private LocationManager locationMgr;
	   private LocationListener locationListener;
	   private Handler handler;
	   private Runnable handlerCallback;
	   private String providerName;
	   private String logTag;
	   /**
	    * Construct with a LocationManager, and a Handler to pass back Messages via.
	    * 
	    * @param locationMgr
	    * @param handler
	    */
	   public LocationHelper(LocationManager locationMgr,Handler handler,String logTag){
		   this.locationMgr=locationMgr;
		   this.handler=handler;
		   this.locationListener=new LocationListenerImpl();
		   //include callback thread,as the handler callback
		   this.handlerCallback=new Thread (){
			   public void run(){
				   endListenForLocation(null);
			   }
		   };
		   // use Criteria to get provider (and could use COARSE, but doesn't work in emulator)
		   // (FINE will use EITHER network/gps, whichever is the best enabled match, except in emulator must be gps)
		   // (NOTE: network won't work unless enabled - Settings->Location & Security Settings->Use wireless networks)
		   Criteria criteria=new Criteria();
		   criteria.setAccuracy(Criteria.ACCURACY_FINE);
		   //establish provider
		   this.providerName=locationMgr.getBestProvider(criteria, true);
		   this.logTag=logTag;
	   }
	   
	   /**
	    * Invoke the process of getting the current Location.
	    * Expect Messages to be returned via the Handler passed in at construction with results.
	    * 
	    * @param durationSeconds amount of time to poll for location updates
	    */
	   public void getCurrentLocation(int durationSeconds){
		   if(this.providerName==null){
			// return 2/0/0 if provider is not enabled
			Log.d(logTag, "Location provideName null, provider is not enabled or not present.");  
			sendLocationToHandler(MESSAGE_CODE_PROVIDER_NOT_PRESENT, 0, 0);
			return;
		   }
		      // first check last KNOWN location (and if the fix is recent enough, use it)
		      // NOTE -- this does NOT WORK in the Emulator
		      // (if you send a DDMS "manual" time or geo fix, you get correct DATE, 
		      // but fix time starts at 00:00 and seems to increment by 1 second each time sent)
		      // to test this section (getLastLocation being recent enough), you need to use a real device
		   Location lastKnown=locationMgr.getLastKnownLocation(providerName);
		   if(lastKnown!=null&&lastKnown.getTime()>(System.currentTimeMillis()-FIX_RECENT_BUFFER_TIME)){
			   Log.d(logTag, "Last known location recent, using it: " + lastKnown.toString());
			   sendLocationToHandler(MESSAGE_CODE_LOCATION_FOUND,(int)(lastKnown.getLatitude()* 1e6),
					   (int)(lastKnown.getLongitude()*1e6));
		   }else{
			   Log.d(logTag, "Last location NOT recent, setting up location listener to get newer update.");
			   listenForLocation(providerName,durationSeconds);
		   }
	   }
	   
	   private class LocationListenerImpl implements LocationListener{
		@Override
		public void onLocationChanged(Location loc) {
			// TODO Auto-generated method stub
			if(loc==null){
				return;
			}
			Log.d(logTag, "Location changed to:" + loc.toString());
			endListenForLocation(loc);
			
		}

		@Override
		public void onProviderDisabled(String provider) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void onProviderEnabled(String provider) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void onStatusChanged(String provider, int status, Bundle extras) {
			// TODO Auto-generated method stub
			Log.d(logTag, "Location status changed to:" + status);
			switch(status){
			case LocationProvider.AVAILABLE:
				break;
			case LocationProvider.TEMPORARILY_UNAVAILABLE:
				break;
			case LocationProvider.OUT_OF_SERVICE:
				endListenForLocation(null);
				break;
			}
		 }
	   }
	   //end listening
	   private void endListenForLocation(Location loc){
		   locationMgr.removeUpdates(locationListener);
		   handler.removeCallbacks(handlerCallback);
		   if(loc!=null){
			   sendLocationToHandler(MESSAGE_CODE_LOCATION_FOUND,(int)(loc.getLatitude()*1e6),(int)(loc.getLongitude()*1e6));
		   }else{
			   sendLocationToHandler(MESSAGE_CODE_LOCATION_NULL, 0, 0);
		   }
	   }
	   
	   private void sendLocationToHandler(int msgId,int lat,int lon){
		   Message msg=Message.obtain(handler,msgId,lat,lon);
		   handler.sendMessage(msg);
	   }
	   //start listen for location updates
	   private void listenForLocation(String providerName,int durationSeconds){
		   locationMgr.requestLocationUpdates(providerName, 0,0, locationListener);
		   handler.postDelayed(handlerCallback, durationSeconds * 1000);
	   }
}

We’re still pinning our hopes on the GPS provider by specifying ACCURACY_FINE, but you could use the lowest criteria you need. If we’d specified ACCURACY_COARSE then the GPS or network providers could be returned。

GPS supersedes network, if both are available.

We pass it our earlier handler callback and the amount of time we want it to delay before firing. This is how we’ll stop the listener after the specified duration is up. This is important: we don’t want to leave the listener there permanently. Listening for
location updates frequently (we set the time and distance stops to 0, which means update as frequently as you can) will consume the battery quickly. In this case, we want to get in, get the information we need, and get out.

This method will alert us when a new location fix has been found. In it, we also stop the location updates, and we send the newly found location along. Using this approach, we don’t wait the entire duration the caller specifies if we quickly get a location
update.

Here, we also stop the location updates, in case the provider is disabledTo boil things down, our helper class checks for the last-known location for the given provider, and if present and recent, returns it. If not, it starts a LocationListener and returns
when either that listener gets a good fix or the duration specified runs out. All the results are returned via the Handler using a Message with constants indicating what happened, as well as data for the latitude and longitude (if present). while we’re already
listening for updates.

抱歉!评论已关闭.