Home‎ > ‎RF Signal Tracker‎ > ‎

    How To: Setup the GPS (a very basic tutorial)

    Let me start by saying I have a VERY imperfect understanding of this topic, and this page is really more for me than you. That said, here is my process for setting up the GPS and getting a Latitude/Longitude. ~ Ken

    MY FIRST ASSUMPTION ABOUT YOUYou know the basics of Android App development. The cool thing about Android is that it has grown up on the web, so there are tutorials and example code everywhere (honestly, about 60-70% of RF Signal Tracker was lifted from examples found all over the web). The problem with that, though, is if you know nothing about Android you may get confused on the basics. It's like knowing nothing about cars and deciding you want to build one from scratch. You find car parts (with detailed explanations of the part) laying all over the place, but you have no idea how they go together.

    Well, for the basics I would suggest Professional Android 2 Application Development (Wrox Programmer to Programmer), by Reto Meier. This will give you a good foundation on Android -- remember, the platform you are building on is a telephone...its primary job is to make and receive calls, so the philosophy is a little different. Read that and learn about Intents and Activities and Broadcast Alerts and such.

    The Basic Steps to Setup the GPS:

    1. Set the permissions in the Manifest.

      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
      <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />


      These permissions are needed so the device can use the "coarse" location where the mobile location is determined by the cell network. This is not very precise, but it is fast and will give your ballpark position (literally) until the slower, more power intensive, but more accurate GPS can get an accurate fix.

    2. Create the location objects.

      LocationManager locationManager;
      LocationListener listenerCoarse, listenerFine;

      I set these as global variables.

    3. Listen to the location objects.

      This is the heart of the exercise. I create a method that gets called when the activity starts. I call it in the onResume() method because this is called the first time the activity is used and every time it resumes after being paused (see activity lifecycle). The method has this structure:

      private void locationSetup(){
                 // set the location manager
                 // Initialize fine criteria for location providers
                 // Initialize coarse criteria for location providers.
                 // set gps update distance & time
                 // get the last known location
                 // setup your listener
                 // setup fine and coarse listeners

      }

      First set the global location manager objects:

      locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

      Now initialize your fine and coarse listener criteria:

      Criteria fine = new Criteria();
      fine.setAccuracy(Criteria.ACCURACY_FINE);
      fine.setAltitudeRequired(false);
      fine.setBearingRequired(false);
      fine.setSpeedRequired(true);
      fine.setCostAllowed(true);

      Criteria coarse = new Criteria();
      coarse.setAccuracy(Criteria.ACCURACY_COARSE);


      With the "fine" criteria I'm asking for ACCURACY_FINE, of course, I don't need altitude or bearing necessarily, but I do want speed. The setCostAllowed() condition is set because not all mobile plans include an unlimited data package, so customers may be charged a per-use fee for data, which includes GPS data. So, it's necessary to ask users if it's okay to incur costs on their behalf for data that your application consumes. I set this to true under the assumption they know they are using data constantly, but you can choose otherwise.

      The coarse criteria is simple, just set to ACCURACY_COARSE. I made the mistake of choosing very strict criteria on this once and did not realize that I effectively made this "coarse" criteria a "fine" criteria. This is important because making the coarse criteria too fine will basically force the app to always use the GPS location handling rather than the looser (and faster) network. What this means in practice is if the user is inside when the app starts he will never be able to determine his location -- GPS does not work indoors.

      Keep the coarse criteria loose, like I have it, and it will locate the mobile in a building and switch over to GPS when the mobile moves outdoors.

      I then set the power level for both:

      fine.setPowerRequirement(Criteria.POWER_HIGH);
      coarse.setPowerRequirement(Criteria.POWER_LOW);


      You can let the user choose these levels with preferences in your code.

      Now set you update intervals:

      int GPS_TIMEUPDATE = 1500; // update gps every 1.5sec
      int GPS_DISTANCEUPDATE = 7; // update gps every 7m


      Now get your last known location:

      String provider = locationManager.getBestProvider(coarse, true);
      Location location = locationManager.getLastKnownLocation(provider);


      This basically sets provider=network which gives you a fast result to the "location" variable based on the last location the device was aware of. This object contains your rough latitude and longitude and a few other things.

      We now set up listeners for coarse and fine location updates:

      if (listenerFine == null || listenerCoarse == null){ createLocationListeners(); }

      When the app starts, listenerFine and listenerCoarse will, of course, be null, so the createLocationListeners() method will be called (we will create that shortly). Once the listeners are initialized we set up the update specifics for the two listeners:

      if(listenerCoarse != null){
                      locationManager.requestLocationUpdates(
               locationManager.getBestProvider(coarse, true),
      GPS_TIMEUPDATE, 
      GPS_DISTANCEUPDATE, 
      listenerCoarse
      );
      }


      if(listenerFine != null){
                      locationManager.requestLocationUpdates(
               locationManager.getBestProvider(fine, true),
      GPS_TIMEUPDATE, 
      GPS_DISTANCEUPDATE, 
      listenerFine
      );
      }


      locationManager.getBestProvider([criteria], [boolean]) returns the name of the provider that best meets the given criteria. Only providers that are permitted to be accessed by the calling activity will be returned. If several providers meet the criteria, the one with the best accuracy is returned. If no provider meets the criteria, the criteria are loosened in the following sequence: power requirement, accuracy, bearing, speed, then altitude. Note that the requirement on monetary cost is not removed in this process.

      GPS_TIMEUPDATE is  the minimum time interval for notifications, in milliseconds. This field is only used as a hint to conserve power, and actual time between location updates may be greater or lesser than this value.

      GPS_DISTANCEUPDATE  the minimum distance interval for notifications, in meters.

      And finally the location listener (listenerFine/listenerCoarse) that is called for every update. All the above code is placed in a try/catch and will look like this:

      locationSetup

          private void locationSetup(){
              try{
                  // set the location manager
              locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

                  // Initialize fine criteria for location providers
                  Criteria fine = new Criteria();
                  fine.setAccuracy(Criteria.ACCURACY_FINE);
                  fine.setAltitudeRequired(false);
                  fine.setBearingRequired(false);
                  fine.setSpeedRequired(true);
                  fine.setCostAllowed(true);
                  
                  // Initialize coarse criteria for location providers.
                  Criteria coarse = new Criteria();
                  coarse.setAccuracy(Criteria.ACCURACY_COARSE);

                  fine.setPowerRequirement(Criteria.POWER_HIGH);
              coarse.setPowerRequirement(Criteria.POWER_LOW);
                  
                  // set gps update distance & time
             int GPS_TIMEUPDATE = 1500; // update gps every 1.5sec
             int GPS_DISTANCEUPDATE = 7; // update gps every 7m

                  // get the last known location
              String provider = locationManager.getBestProvider(coarse, true);
                  Location location = locationManager.getLastKnownLocation(provider); // your initial location
             
                  // setup your listener
                  if (listenerFine == null || listenerCoarse == null){ createLocationListeners(); }

                  // Will keep updating about every GPS_TIMEUPDATE ms until accuracy is 
                  // about GPS_DISTANCEUPDATE meters to get quick fix.
                  if(listenerCoarse != null){
                      locationManager.requestLocationUpdates(
                      locationManager.getBestProvider(coarse, true), 
                  GPS_TIMEUPDATE, 
                  GPS_DISTANCEUPDATE, 
                  listenerCoarse
                  );
                  }
                  
                  // Will keep updating about every GPS_TIMEUPDATE ms until accuracy is 
                  // about GPS_DISTANCEUPDATE meters to get accurate fix.
                  if(listenerFine != null){
                      locationManager.requestLocationUpdates(
                      locationManager.getBestProvider(fine, true), 
                  GPS_TIMEUPDATE, 
                  GPS_DISTANCEUPDATE, 
                  listenerFine
                  );
                  }
                  
              }catch(Exception e){
              e.printStackTrace();
              }
          }


      Next we create the method that defines the location listeners. This method will automatically use either a fine or coarse location listener based on the accuracy it is currently receiving:

      createLocationListeners

          /**
           *   Creates LocationListeners
           */
          private void createLocationListeners() {

          listenerFine = new LocationListener() {
             public void onStatusChanged(String provider, int status, Bundle extras) {}
             public void onProviderEnabled(String provider) {}
             public void onProviderDisabled(String provider) {}
             public void onLocationChanged(Location location) {
                 if (location.getAccuracy() > 500 && location.hasAccuracy()){
                     locationManager.removeUpdates(listenerFine);            
                 }else{
                     // do something with your location update
                 }
             }
          };

          listenerCoarse = new LocationListener() {
             public void onStatusChanged(String provider, int status, Bundle extras) {}
             public void onProviderEnabled(String provider) {}
             public void onProviderDisabled(String provider) {}
             public void onLocationChanged(Location location) {
                 if (location.getAccuracy() < 500 && location.hasAccuracy()){
                     locationManager.removeUpdates(listenerCoarse);            
                 }else{
                     // do something with your location update
                 }

             }
          };
          }



    4. Stop listening to the location objects.

      Finally a best practices is to turn off your listeners if you pause or exit your activity, otherwise the user will see the GPS icon in the notification bar continue to blink after leaving the app.

              if(locationManager != null){
                   locationManager.removeUpdates(listenerCoarse);
                   locationManager.removeUpdates(listenerFine);
              }

      Enclose this in a try/catch and you should be good. I like to set this in a separate "stopListening()" method that gets called when I exit or pause the activity or leave the activity.

    That's it! Enjoy!

    Comments