How the Android Gps Tracker Client Works

I finally had the time to rewrite the android client and to get the background service working as I’ve wanted to for a long time. Gps Tracker is now based on work I had done previously on my other website, mycelltracker.com. The big difference between the old mycelltracker app and the new Gps Tracker is the introduction of Google Play location services.

With that I want to go into an in-depth discussion of how Gps Tracker now works on Android devices. We’ll start by looking at the structure of the application beginning with the five classes used in the app. I’ll first give a brief description of each. Here are the class files on github if you want to follow along:

GpsTracker android client class files on github

GpsTrackerActivity – This is the one and only screen of the application. It displays two text fields. One for the user name and one for the upload website. There are a set of radio buttons that allow the user to set the interval (from one minute, 5, 15, 30, 60). And there is a start tracking button.

Android Gps Tracker

GpsTrackerAlarmReceiver – This class has an onReceive method that is called periodically from a repeating AlarmManager. When the method is called, the background service is started.

GpsTrackerBootReceiver – This class also has an onReceive method that is called but this one is called when the phone is rebooted. When the method is called, it creates a new AlarmManager. The AlarmManager starts the background service periodically using the receiver class above.

LocationService – This background service is called periodically from the AlarmManager above. When it starts, it uses google play location services to get the location of the device and send it to the update website using an open source asynchronous http client library. The service then terminates itself.

LoopjHttpClient – This class wraps a third party library that does asynchronous http calls.

The first thing that happens is that the main activity starts up and in onCreate, a sharedPreference file is opened and the currentlyTracking variable is set:

SharedPreferences sharedPreferences = this.getSharedPreferences("com.websmithing.gpstracker.prefs", Context.MODE_PRIVATE);
currentlyTracking = sharedPreferences.getBoolean("currentlyTracking", false);

This is an important variable and will be used throughout the application. When onResume is called, two other methods are called. The first is displayUserSettings which just restores the UI to its previous state and the second is setTrackingButtonState which displays the correct button state (either tracking or not tracking). When a user taps on the start tracking button, the currentlyTracking variable is set to true and the method startAlarmManager is called:

    
private void startAlarmManager(Context context) {
    Log.d(TAG, "startAlarmManager");
    Context context = getBaseContext();
    alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    gpsTrackerIntent = new Intent(context, GpsTrackerAlarmReceiver.class);
    pendingIntent = PendingIntent.getBroadcast(context, 0, gpsTrackerIntent, 0);

    alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
        SystemClock.elapsedRealtime(),
        intervalInMinutes * 60000, // 60000 = 1 minute
        pendingIntent);
}

In this method, a repeating AlarmManager is created and the interval is set to the variable intervalInMinutes. intervalInMinutes is chosen by the user using the radio buttons on the screen. The interval is set to 1 minute, 5, 15, 30 or 60. The AlarmManager class is an interface to the phone’s system alarm services and allows us to run code at a specific time even if the GpsTracker app is not running. In this method, we create a pending intent which allows AlarmManager to run our code using our application’s permissions. So what happens here is that every one minute, for instance, the alarmManager will execute the onReceive method of the GpsTrackerAlarmReceiver class.

Let’s take a look at the GpsTrackerAlarmReceiver class. First of all you’ll see that it extends the WakefulBroadcastReceiver class. This is very important. This is a helper class that creates a partial wake lock. Creating this wake lock insures that the cpu doesn’t go back to sleep while the background service is running. You need to set this permission in the AndroidManifest file:

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

As you can see the GpsTrackerAlarmReceiver class is very simple, with only one method.

public class GpsTrackerAlarmReceiver extends WakefulBroadcastReceiver {
    private static final String TAG = "GpsTrackerAlarmReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        context.startService(new Intent(context, LocationService.class));
    }
}

When the alarmManager calls the onReceive method it starts the LocationService background service. If it’s already started, like if you have the interval set to one minute, then it only calls the onStartCommand method in the LocationService class. As long as the phone is running, the alarmManager will continue to start this service over and over again, get the phone’s location and then send the location to your update website.

What happens if somebody turns the phone off and then back on again, will we still get location updates? Well, glad you asked and the answer is yes, so let me show you how it’s done. There is another class called GpsTrackerBootReceiver which extends BroadcastReceiver. This time we will not use the wakeful receiver because all we are doing is creating a repeating alarmManager. This is a very fast operation and since we are not starting a service as in the previous class, we do not need to create an additional wake lock.

Note also that we are checking the variable currentlyTracking:

        
if (currentlyTracking) {
    alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
        SystemClock.elapsedRealtime(),
        intervalInMinutes * 60000, // 60000 = 1 minute,
        pendingIntent);
} else {
    alarmManager.cancel(pendingIntent);
}

if we are currently tracking (as set by the user in the gpsTrackerActivity screen when they tap on the start tracking button), then start the alarmManager, otherwise cancel it.

This broadcast receiver’s onResume method is called every time the phone is rebooted. We’ll need to look at the AndroidManifest.xml file for a moment to see how that happens.

<receiver android:name=".GpsTrackerBootReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

Here you can see we have registered a receiver with the name of our class, .GpsTrackerBootReceiver (don’t forget the period before the class name), and this receiver has a filter that says only listen to BOOT_COMPLETED events. So every time the phone is rebooted, call my onReceive method.

While we are looking in the AndroidManifest, note that GpsTrackerAlarmReceiver is also registered and the LocationService as well:

<service android:name=".LocationService">
</service>

This brings us to the next class in the application, the LocationService. This class extends Service and runs on the main thread of the application. It runs in the background and will continue running even if the application shuts down. Since it runs on the main thread, any long running operations should be on their own threads or else the user could experience having the app’s UI freeze up (something you want to avoid…). This service does two things:

a) gets the phone’s location using Google play location services
b) sends the location data to the upload website

The first operation uses the requestLocationUpdates method of the LocationClient class to start getting location data from the phone. When the phone has a location that meets the criteria that we specify then it returns that location back to a callback method. That method’s name is onLocationChanged. This all happens asynchronously so we never have to worry about freezing up the UI with this operation.

The second operation uses this cool open source library:

Android Asynchronous Http Client

written by James Smith. As you can see from the name of the library, this allows us to make http calls asynchronously so once again, we don’t have to worry about tying up the UI. Also, this library handles being called from a background service. One less thing we have to worry about.

So when the service starts, it calls onCreate (like an activity) and then calls onStartCommand. If the service is already running and something else tries to start the service, a new service is not created, what happens is onStartCommand is called again. This is where I call the startTracking method from:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (!currentlyProcessingLocation) {
        currentlyProcessingLocation = true;
        startTracking();
    }
    return START_STICKY;
}

I first check the variable currentlyProcessingLocation. If the service has already been started (which might happen when we have an interval of one minute) then we don’t want to call startTracking twice. Sometimes it can take more than a minute to get that first satellite fix if we are using gps. So, the moment we start tracking, we set that variable to true and that solves the problem. START_STICKY means that if the service is somehow killed, then the system will restart the service and the service can continue doing what it’s supposed to do.

The next thing we do is create our locationClient. Once the client is connected we can then create a LocationRequest object in the onConnected callback method. Let’s take a look at the properties we are setting on the locationRequest object:

@Override
public void onConnected(Bundle bundle) {
    Log.d(TAG, "onConnected");

    locationRequest = LocationRequest.create();
    locationRequest.setInterval(1000); // milliseconds
    locationRequest.setFastestInterval(1000); // the fastest rate in milliseconds at which your app can handle location updates
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

    locationClient.requestLocationUpdates(locationRequest, this);
    }

We set our interval to 1 second and our priority to PRIORITY_HIGH_ACCURACY. This means get the highest accuracy possible as quickly as possible. So the phone will be trying to get it’s location from the google play’s fused location provider. Fused means that the location can be coming from either gps, wifi, cell towers or other means. In the new google play location services, you no longer worry about which provider to use, you simply tell it what accuracy you want and it will figure it out on it’s own. I tell you this is a welcome relief from how things have been in the past. Stackoverflow has countless questions about which provider to use (gps vs. wifi vs. cell tower etc…) and it has always been a serious point of confusion for lots of developers, so good riddance.

You’ll notice that I’m using requestLocationUpdates instead of something like getLastLocation. I wanted the ability to filter my location by accuracy. That’s why I have the if block here in the onLocationChanged method:

if (location.getAccuracy() < 100.0f) {
    stopLocationUpdates();
    sendLocationDataToWebsite(location);
}

to only accept a location that has an accuracy of 100 meters or better. This can be adjusted to fit your own needs. onLocationChanged is going to be called about once a second (since we’re using requestLocationUpdates), so once we have a location that meets our criteria, let’s send that location to the update website and shut down the service.

In the sendLocationDataToWebsite method, we create a RequestParams object, which is part of the loopj async http library and set all of our parameters for our POST http operation. We do our async post and on either success or failure, we shut down the service with this line:

stopSelf();

One thing you’ll notice within the sendLocationDataToWebsite method is a little if/else block that checks if firstTimeGettingPosition is true. If it is true, then we need to save our latitude and longitude to shared preferences only. If it’s not true, then we need to create a new location object with our previously saved latitude and longitude. Once we have this, we can use the distanceTo method of the Location class to figure out how far we have moved since the last location update. This allows us to calculate our total distance traveled.

if (firstTimeGettingPosition) {
    editor.putBoolean("firstTimeGettingPosition", false);
} else {
    Location previousLocation = new Location("");

    previousLocation.setLatitude(sharedPreferences.getFloat("previousLatitude", 0f));
    previousLocation.setLongitude(sharedPreferences.getFloat("previousLongitude", 0f));

    float distance = location.distanceTo(previousLocation);
    totalDistanceInMeters += distance;
    editor.putFloat("totalDistanceInMeters", totalDistanceInMeters);
}

editor.putFloat("previousLatitude", (float)location.getLatitude());
editor.putFloat("previousLongitude", (float)location.getLongitude());
editor.commit();

When the alarmManager reaches its interval again, the background service is recreated and the whole process starts again. This process works well on larger intervals such as five minutes, on shorter intervals like one minute, there is a chance that it could take google play location services a longer time to get a location fix that meets our criteria. What I’m getting at is that for shorter intervals, it might be less of an energy drain to keep the background service running continuously and keep google play location services running continuously. It may be less of a battery drain. The two scenarios need to be tested.

As the application stands now, I think it’s a good starting point for any further work. It works reliably and periodically in the background and restarts when the phone reboots. This would be a good starter app for other apps. If you want to see this app in action, you can download it from google play:

https://play.google.com/store/apps/details?id=com.websmithing.gpstracker

and then start tracking. Then go to the test webpage and search for your username in the routes dropdown box below the map. It should be near the top of the list:

https://www.websmithing.com/gpstracker/displaymap.php

The full source code can be found in my repo on github in the phoneClients android directory.

46 thoughts on “How the Android Gps Tracker Client Works

  1. Shawn

    I am a new learner of android and i am about to create a gps tracker for cell as my final year project and i would like if the displaymap.php can be embedded to a webpage for now only can be know the location from the page after all your tutorial are great help to me as a new learner thank you

    Reply
  2. Isuru Chamara

    Hey Nick,
    I have installed your GPS tracker application in my phone and I gave a username and pressed the button. But when I looked up in the test website, route with my username wasn’t there in the drop down menu. What should I do? please help. Also I have other questions,

    1) in your article , you have said that,
    “So the phone will be trying to get it’s location from the google play’s fused location provider. Fused means that the location can be coming from either gps, wifi, cell towers or other means”
    …that means can we track the phone even without turning on the GPS? I am confused here.

    2) what do you mean by “get a location fix that meets our criteria” ? what is this “criteria” ?

    3) so, if we are to further develop this to use as a recovery software to recover stolen mobile device. We do not want the thief to be aware of this application, right? How can we hide the “notification” which says “searching using GPS” in the notification drop down screen which get displayed as soon as I turned on GPS. Literally what I need is to hide this application from whatever the user except me. So is there any way to hide notifications and all these stuff?

    Reply
    1. kamal

      I have installed your GPS tracker application in my phone and I gave a username and pressed the button. But when I looked up in the test website, route with my username wasn’t there in the drop down menu. What should I do? please help.

      kamal

      Reply
  3. Jeroen

    Hi Nick,

    First of all, great work with GpsTracker!
    I’ve been playing around with the PHP server package.
    My question: how can i change the query (procedure) or script to see the most actual markers by clicking “view all” Button? (the last known position…)
    Now i see only the first known position of the marker.

    Thanks in advance!

    Reply
    1. Nick Fox Post author

      Hey Jeroen

      I’m not sure what you mean by “most actual markers”. Do you mean the most recent markers? This query will give you all the active routes and will display the most recent marker in red.

      Nick

      SELECT DISTINCT(sessionId), MAX(gpsTime) gpsTime,
      CONCAT(‘{ “latitude”:”‘, CAST(latitude AS CHAR),'”, “longitude”:”‘, CAST(longitude AS CHAR), ‘”, “speed”:”‘, CAST(speed AS CHAR), ‘”, “direction”:”‘, CAST(direction AS CHAR), ‘”, “distance”:”‘, CAST(distance AS CHAR), ‘”, “locationMethod”:”‘, locationMethod, ‘”, “gpsTime”:”‘, DATE_FORMAT(MAX(gpsTime), ‘%b %e %Y %h:%i%p’), ‘”, “userName”:”‘, userName, ‘”, “phoneNumber”:”‘, phoneNumber, ‘”, “sessionID”:”‘, CAST(sessionID AS CHAR), ‘”, “accuracy”:”‘, CAST(accuracy AS CHAR), ‘”, “extraInfo”:”‘, extraInfo, ‘” }’) json
      FROM gpslocations
      WHERE sessionID != ‘0’ && CHAR_LENGTH(sessionID) != 0 && gpstime != ‘0000-00-00 00:00:00’
      GROUP BY sessionID
      ORDER BY gpsTime asc;

      Reply
  4. Febrian

    Hi Nick,

    First of all Thanks for this software,
    I have 1 question: is there is a way to draw lines between markers ?

    Reply
  5. richard

    hi thanks for the nice project just downloaded … will try it … just want to know does it send the location to server even if i close the Gps tracker Apps ? or In order to send the location to server apps must be running ? …

    thank you

    Reply
  6. mike

    Hi.

    Thank you for the nice app and web IF.
    It is a bunch of work to create and debug this for us.

    I am a windows developer and use the android Samsung S3 phone for this.
    I have built it with little trouble in AS 0.0.14 with all extras installed.
    I am able to run the app on my S3.
    However, my name/location does not show up in your map despite logcat showing success.
    Logcat is all OK. This is after 30 minutes and many “successful” posts to your website.

    Could you please explain this?

    Regards,

    Mike

    Reply
    1. Nick Fox Post author

      Hi Guiseppe, yes the android app does work in the background and it will also restart on it’s own if you restart the phone.

      n

      Reply
  7. Tydeus

    Hi Nick,
    It seems that “Google Play Services” are required on phone to acquire position.
    Is there another method to get fix point ?

    Regards,

    Marc T.

    Reply
  8. Roland Anthony

    I’m trying to track a friend. Can I track my friend’s cell phone and auto. through your software; if so how? Do I need to physically install the software on his or her phone? Can I use my phone to track my friend. How do I input their phone number to be track?

    Reply
    1. Nick Fox Post author

      Hey Roland

      If you want to track a phone, you need to install software on that phone. That goes for my software or for anyone else’s. If you want to track a car, you can install an ODB tracker. Go to amazon.com and search for ODB tracker. They are easy to install and use.

      n

      Reply
  9. arehookey

    Hey Nick,
    thanks for the nice project, i could not try on hosting does not view map/blank map, it checks also
    file php/getroutes.php no problem. Please advise
    listen to your review,
    I think to using in my car, how do configure this project to connect device TRACKER ODB?

    Reply
  10. Adrian

    Hi Nick,

    I am looking to allow my self-signed certificate to be accepted by the gpstracker android client. In my case, I am running this off a raspberry pi and I am on a dynamic dns.

    Ive looked at many stackoverflow articles about using loopj and mysslsocketfactory but I cant make heads or tails on how to implement it in this particular application (apart from specifying: new AsyncHttpClient(true, 80, 443) – but this accepts all certs…Is there a way I can have this application accept my self signed cert from my domain only in the loophttpclient portion of the gpstracker code?

    TIA,
    Adrian

    Reply
    1. Nick Fox Post author

      Hi Adrian. Just buy a normal cert for 10 dollars a year from any of the domain providers. Easiest way.

      n

      Reply
      1. Adrian

        That is definitely true, however for dynamic ddns its a little more costly per year and some providers dont offer it. However I just implmented this code in the loophttpclient java file.

        import com.loopj.android.http.MySSLSocketFactory;

        public class LoopjHttpClient {
        private static AsyncHttpClient client = new AsyncHttpClient();

        public static void get(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {

        \\NEW CODE WHICH ACCEPTS SELF SIGNED CERTS
        client.setSSLSocketFactory(MySSLSocketFactory.getFixedSocketFactory());

        client.get(url, params, responseHandler);

        Reply
  11. Raul Barnutiu

    Hello Nick,

    Great job on this project, it’s been really helpful to me.
    I can’t figure out one thing, though.

    I can see that it doesn’t matter if I move for 5 minutes and the suddenly stop, the speed is recorded in that exact moment when the location is acquired.

    How would I go about registering say the maximum speed in a 5 minute interval?
    The thing is, if I’m in my car… drive for 5 minutes, and then stop at a stop sign or red light, the recorded speed will be always 0. I would like it to show my max speed during that 5 minute interval.

    Thank you!

    Reply
    1. Nick Fox Post author

      Hey Raul

      Well, let’s assume you are getting data points every minute. What you can do is create an array and store the speed in that array every minute. Then at a five minute interval, you can go and check the array for the max value. That will be your top speed. After you have gotten that value, you can empty the array and start over again for the next five minute period. That’s quick and off the top of my head but I think something like that would work.

      n

      Reply
  12. Adrian

    Hi Huys,

    Now at the point where im modifying the map.js file to center the map on the last location upon refresh. Ive tried tinkering around with this code snippet in the maps.js file:

    // want to set the map center on the last location
    if (counter == $(json.locations).length) {
    gpsTrackerMap.setView(tempLocation, 15);
    //if using fixed zoom
    finalLocation = true;

    But ran into a problem where the map wouldnt center on the last location. I needed however to comment out the lines below as they were overriding the center location code above:

    // fit markers within window
    //var bounds = new L.LatLngBounds(locationArray);
    //gpsTrackerMap.fitBounds(bounds);

    Nick, is this the correct way to do this?

    Thanks,
    Adrian

    Reply
  13. amirmehdi

    Hello i installed the GPS, every thing seems to be working , i can see the 3 tracker on the map, i installed the android app, i put in the web location of mine . what should be in the username? i cant see my cellphone on that map

    Reply
    1. Nick Fox Post author

      The username can be anything you want. Make sure you test the phone on the websmithing website first to see if it’s working correctly.

      n

      Reply
      1. kamal

        I have installed your GPS tracker application in my phone and I gave a username and pressed the button. But when I looked up in the test website, route with my username wasn’t there in the drop down menu. What should I do? please help.

        kamal

        Reply
  14. Evert

    Hi Nick,

    On the testsite I can see my location, on my own setup it isn’t showing any new location in the database.

    Do you have any idea?

    Best regards, Evert

    Reply
  15. Al

    Hi,

    Is it compatible with android 6.0.1? I tried using the test site without resulta, but using a phone with android 5 works as a charm.

    Kind regards,

    Reply
  16. Al

    Hi,

    Is it compatible with android 6.0.1? I tried using the test site without results, but using a phone with android 5 works as a charm.

    Kind regards,

    Reply
  17. Al

    Hi,

    Never mind, took a while to load the route. Works perfectly.

    I do have a question it is possible to launch the client on by default?

    Kind regards,

    Al

    Reply
  18. Abera

    I have tried by android studio 2.3.3. But it display “unfortunately GpsTracker has been stopped ” when i run it . What should I do? please help.

    Reply
    1. Abera

      Hello i installed the GPS, every thing seems to be working , i can see the 3 tracker on the map, i installed the android app, i put in the web location of mine . what should be in the username? i cant see my cellphone on that map

      Reply
    2. sam dare

      Hello Abera, I had same issue but sorted it as follows. The reason why its stopping is cuz of permission on your phone. Go Settings,Apps,look for Gps Tracker,u should see uninstall and force stop buttons.
      scroll down , click permissions and turn it on.

      App will work.

      Reply
  19. Michele Tran

    Hi Nick I really really need your help! I am not tech savvy at all actually I have no idea anything about what you’re talking about all I know is I want to track two numbers at all time is there easy way that you can teach me or if you’re located anywhere in California San Francisco perhaps please let me know! Thank you so very much

    Reply
  20. sam dare

    Hello Nick,
    First of all thank u for sharing this project. Its really amazing.
    I downloaded script, set up php server and works well using android app on play store.
    I compile android client, using android studio 3.0 and app installed well on phone and launches well. But when i click on button to turn it on, it crashes, Says GPS Track has stopped working. I never changed anything. Any assistance will be highly appreciated.

    Thank U

    Reply
  21. Harv

    Hello Nick,
    First of all thank u for sharing this project.
    How to use the FusedLocationProviderClient in place of the deprecated FusedLocationProviderAPI in this project?

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *


This site uses Akismet to reduce spam. Learn how your comment data is processed.