Realtime Android Geolocation Tracking with the Google Maps API

Realtime mapping and geolocation tracking has become a core feature of web and mobile apps across every industry. The idea is simple – detect and stream location data to a live-updating map to smoothly watch location updates as they change in the real world. With that, Android geolocation tracking is essential.

In this tutorial, we’ll create a simple, location sharing application for Android using PubNub and the Google Maps API. Our application will use PubNub to listen on a channel in Android, and use the Google Maps SDK to draw those locations in realtime, including a flightpath (history of where the device has traveled).

If JavaScript is more your style, we’ve got the same tutorial for JavaScript geolocation tracking.

In the end, we’ll have basic functionality that looks like this:

android geolocation tracking tutorial google maps api

Android Geolocation Tracking with Google Maps

Mapping services are extremely complex, so it makes a lot of sense to use one that is already well-established, robust and feature complete. We’ll use the Google Maps Android API – it has a ton of functionality, and is very easy to get started (but you probably already knew that). Our example app is based on the Google Maps marker example available here.

If you’d prefer a different mapping API, we also recommend Mapbox or EON. Both play beautifully with PubNub.

Getting Your PubNub Developer Keys

To get started, you’ll first need your PubNub publish and subscribe keys. If you don’t have an account, you can sign up here, and your pub/sub keys are available in the Admin Dashboard. The keys look like UUIDs and start with “pub-c-” and “sub-c-” prefixes respectively. Keep those handy – you’ll need to plug them in when initializing the PubNub object in your HTML5 app below.

About the PubNub Android API

PubNub plays together really well with Android because the PubNub Android SDK (part of the PubNub Java SDK family) is extremely robust and has been battle-tested over the years across a huge number of mobile and backend installations. The SDK is currently on its 4th major release, which features a number of improvements such as fluent API, increased performance, sync/async operation, server-side filtering, streamlined logging & debugging, and more.

The PubNub Java SDK is distributed via the Maven Central repository, so it’s easy to integrate with your application whether you’re using Maven or Gradle (hopefully Gradle!).

Maven pom.xml dependency:

<dependency>
  <groupId>com.pubnub</groupId>
  <artifactId>pubnub</artifactId>
  <version>4.2.3</version>
</dependency>

Groovy dependency:

compile group: 'com.pubnub', name: 'pubnub', version: '4.2.3'

Getting Started with Google Maps Location Tracking

The next thing you’ll need to get started with Google Maps services is an API Key to use the API.

  • Go to the Google API Console.
  • Create or select a project, and click Continue to enable the API and related services.
  • On the Credentials page, get an API key and keep it handy for the steps below.

Overall, it’s a pretty quick process. And free to get started, which is a bonus!

Diving into the Code

To run the whole app, you’ll want to grab the code from our GitHub repository.

git clone https://github.com/sunnygleason/pubnub-android-maps.git

The first thing you should do after saving the code is to replace two values in the MainActivity.java:

  • YOUR_PUB_KEY: with the PubNub publish key mentioned above.
  • YOUR_SUB_KEY: with the PubNub subscribe key mentioned above.

Then, update one value in googlemapsapi.xml:

  • YOUR_MAPS_API_KEY: with the Google Maps API key mentioned above.

If you don’t, the app will not be able to communicate with anything and probably clutter your console log with entirely too many errors.

Application Manifest

Let’s dig into the Android Manifest inside AndroidManifest.xml. Here we specify three permissions for the app:

  • Internet (for talking to PubNub and loading maps)
  • Wake lock (for locking the application if we choose to customize that behavior in the future)
  • Access Fine Location (we’re not using it in this app, but you may choose to use Google Play Location APIs for location detection)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.pubnub.example.android.datastream.mapexample.pubnubandroidmap">

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

We have one Main Activity, a Login Activity for specifying a user name, and load the Google Maps API Key from a string resource value.

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".LoginActivity"
            android:configChanges="orientation|keyboardHidden"
            android:screenOrientation="portrait" />
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key" />
    </application>

</manifest>

And that’s it! Pretty straightforward so far.

The Geolocation Tracking User Interface

Here’s what we intend the UI to look like:

android geolocation tracking tutorial

The UI is super minimal – we just place the Map component into the main layout.

    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.SupportMapFragment" />

On to the Activity code!

The Java Activity Code

Right on! Now we’re ready to dive into the Java Activity code. It’s not a ton of code, so this should hopefully be pretty straightforward.

The first lines we encounter set up the application constants: a TAG for logging, keys for identifying Android settings, our PubNub publish and subscribe keys mentioned above, and the channel we use for receiving location messages.

NOTE: make sure this matches your location broadcasting feature!

The next few set up our working variables: a GoogleMap component, a PubNub API client, SharedPreferences for our app, and a Marker, PolyLine and List of LatLng points for our flight path.

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
    public static final String TAG = MainActivity.class.getName();

    public static final String DATASTREAM_PREFS = "com.pubnub.example.android.datastream.mapexample.DATASTREAM_PREFS";
    public static final String DATASTREAM_UUID = "com.pubnub.example.android.datastream.mapexample.DATASTREAM_UUID";

    public static final String PUBLISH_KEY = "YOUR_PUB_KEY";
    public static final String SUBSCRIBE_KEY = "YOUR_SUB_KEY";
    public static final String CHANNEL_NAME = "maps-channel";

    private GoogleMap mMap;

    private PubNub mPubNub;

    private SharedPreferences mSharedPrefs;

    private Marker mMarker;
    private Polyline mPolyline;

    private List<LatLng> mPoints = new ArrayList<>();

Next up, we define our onCreate() initialization handler. The initialization consists of creating a new Map object and initializing PubNub.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mSharedPrefs = getSharedPreferences(DATASTREAM_PREFS, MODE_PRIVATE);
        if (!mSharedPrefs.contains(DATASTREAM_UUID)) {
            Intent toLogin = new Intent(this, LoginActivity.class);
            startActivity(toLogin);
            return;
        }

        setContentView(R.layout.activity_main);

        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        initPubNub();
    }

The onMapReady() handler just sets the Map component variable.

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
    }

Initializing PubNub requires a few steps. First off, we initialize the simple variables in our PubNub client.

    private final void initPubNub() {
        PNConfiguration config = new PNConfiguration();

        config.setPublishKey(PUBLISH_KEY);
        config.setSubscribeKey(SUBSCRIBE_KEY);
        config.setSecure(true);

        this.mPubNub = new PubNub(config);

Then, we add a message event handler to process messages from our soon-to-be-subscribed channel. This handler calls updateLocation(), which we’ll describe shortly.

        this.mPubNub.addListener(new SubscribeCallback() {
            @Override
            public void status(PubNub pubnub, PNStatus status) {
                // no status handler for simplicity
            }

            @Override
            public void message(PubNub pubnub, PNMessageResult message) {
                try {
                    Log.v(TAG, JsonUtil.asJson(message));

                    Map<String, String> map = JsonUtil.convert(message.getMessage(), LinkedHashMap.class);
                    String lat = map.get("lat");
                    String lng = map.get("lng");

                    updateLocation(new LatLng(Double.parseDouble(lat), Double.parseDouble(lng)));
                } catch (Exception e) {
                    throw Throwables.propagate(e);
                }
            }

            @Override
            public void presence(PubNub pubnub, PNPresenceEventResult presence) {
                // no presence handler for simplicity
            }
        });

Lastly, we perform the channel subscription.

        this.mPubNub.subscribe().channels(Arrays.asList(CHANNEL_NAME)).execute();
    }

Now we’re ready to receive incoming events!

To make the map live-updating, we need to implement the updateLocation() function. In our case, we update the marker to reflect the latest point, update the polyline with the latest segment, and move the camera to re-center the map on the marker. Pretty easy!

The only tricky bit worth mentioning is that we call runOnUiThread() to ensure the updates are done on the UI thread. Otherwise, we’ll get application errors (because the inbound messages aren’t being received on the UI thread).

    private void updateLocation(final LatLng location) {
        this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mPoints.add(location);

                if (MainActivity.this.mMarker != null) {
                    MainActivity.this.mMarker.setPosition(location);
                } else {
                    MainActivity.this.mMarker = mMap.addMarker(new MarkerOptions().position(location));
                }

                if (MainActivity.this.mPolyline != null) {
                    MainActivity.this.mPolyline.setPoints(mPoints);
                } else {
                    MainActivity.this.mPolyline = mMap.addPolyline(new PolylineOptions().color(Color.BLUE).addAll(mPoints));
                }


                mMap.moveCamera(CameraUpdateFactory.newLatLng(location));
            }
        });
    }

At this point, we have a functioning map listener with plotting functionality! We just need to implement the broadcast feature somewhere.

Depending on where you want your broadcast to happen, all you need to do is initialize a PubNub client and start sending messages to the channel. In our case, each message contains lat and lng coordinates for the map display.

Not too shabby for 140 lines of Java activity code!

Node.js Broadcaster for Easy Testing

Here’s a tiny example of a Node.js location broadcaster that starts at the Golden Gate Bridge and moves Northeast. You’ll need to install PubNub using npm install pubnub, of course!

pn = require('pubnub');

lat = 37.8199;
lng = -122.4783;

p = new pn({
  publishKey: "YOUR_PUB_KEY",
  subscribeKey: "YOUR_SUB_KEY"
});

setInterval(function() {
  lat = lat + 1;
  lng = lng + 10;
  return p.publish({
    channel: "maps-channel",
    message: {
      lat: lat.toString(),
      lng: lng.toString()
    }
  }, function() {
    return console.log(arguments);
  });
}, 1000);

Android Geolocation Tracking

For folks who want to integrate Android geolocation directly from the application, there’s a sample LocationHelper class available here as part of the extended PubNub Fabric demo.

The basic idea is that we initialize the LocationHelper in our MainActivity, then use onLocationChanged() events to broadcast or respond to the location events from Android.

  this.mLocationHelper = new LocationHelper(this, new LocationListener() {...}); 

For more information on Android Location-Awareness, check out the documentation here.

That concludes this tutorial on realtime tracking with the Google Maps API for Android. Geolocation is definitely one of our favorite subjects, and we’ve got a number of other tutorials around it. Enjoy!

Try PubNub Today

Connect up to 100 devices for Free