1

Explanation/Scenario:

I am developing a app that needs to get in real-time(like 3sec's update) the location of all users of mine app, and these users knowing that, too, will see the another users location, in a map, for example:

map example

This location, needs to be in real-time, because all of the users have access to the map, they need to know where are the users of the app, in the exact moment, to find them selves.

Question:

What i need to do to make my application get in real time the location of all of the users in gps coordinates(for later use on the map)?

Obs:

I am not asking a full application, i only want to know how to get the coordinates each user, by the way, if a answer containing how to put the coordinates on the map, it will be a lot useful! and will get bounty

Community
  • 1
  • 1
Paulo Roberto Rosa
  • 3,071
  • 5
  • 28
  • 53

1 Answers1

21

You want your users to retrieve their coordinates at a fixed interval, so you'll (minimally) need:

  • A good central server (or servers). Take in account that sending a position each 3 seconds and having (for instance) 100 users will result in a good burst of information requests, so it will also need a good infrastructure.

  • Some technology to broadcast the info to your clients. Once you got the information, you'll want to spread it amongst your users, to let them know who's near them.

  • Some technology to store the information that you got from your users: The most according one: a database.

So, step by step:

What kind of coordinates get?

There are two types of location coordinates:

  1. GPS Location Provider
  2. Network Location Provider

Network Location is usually faster than GPS in getting the coordinates information. Additionally, GPS might be very very slow in some situations like for example in-door locations and what's worse, it will drain battery fairly faster. Network location provider, however, is dependent of the cell tower and in fact that's the value it will return (not your real current location), so GPS location is fairly more accurate. I recommend using this latter.

How to get coordinates?

That's a subject that has been solved many times on SO, simply doing a search you'll a lot of info. Regarding to sending the user's location every X time, it might be done with LocationManager. So you'd define something like this:

LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, REFRESH_TIME, MIN_DISTANCE, locationListener);

This will request for user's current position, refreshed every REFRESH_TIME (it's a custom constant, you might set it to whatever you want, but you'll have to tune it up depending on your remote server's technical capacity), with a MIN_DISTANCE (same goes here, it's a constant) as the minimum distance between location updates (in meters), and results should be provided to a Listener that will process the result. This listener should look this way:

private final LocationListener locationListener = new LocationListener() {
  @Override
  public void onLocationChanged(final Location location) {
    ...
  }
};

So, each time the user's position changes in accordance to the 2 above parameters defined, this onLocationChanged() method will be called.

Don't forget to add this line to your AndroidManifest file:

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

Ok, and what do I put inside this method?

That's where you initiate a request to the remote server informing of your current position. The easiest way is deploying a web-server which will have a web-service in your favorite language (PHP, Python...) and will process the request accordingly. The easiest way to inform is doing a simple HTTP POST request to the web-service, so the code inside would be something like this:

final HttpClient httpclient = new DefaultHttpClient();
final HttpPost httppost = new HttpPost("http://www.yourremoteserver.com/mywebservice.php");

try {
  final List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
  nameValuePairs.add(new BasicNameValuePair("Latitude", String.valueOf(location.getLatitude()));
  nameValuePairs.add(new BasicNameValuePair("Longitude", String.valueOf(location.getLongitude()));

  httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
  HttpResponse response = httpclient.execute(httppost);
}
catch (final ClientProtocolException e) { e.printStackTrace(); }
catch (final IOException e) { e.printStackTrace(); }

That's it, now your remote server will be receiving your data. But there's something else that's missing: How to identify each user? Depending on your implementation, you might want to send the user's IP address, or what is more likely, the username. So in that latter case, you'd add another BasicNameValuePair like this:

nameValuePairs.add(new BasicNameValuePair("username", methodThatReturnsUsersName());

If you want to use the IP address, you don't need to send it, better rely on the information that the server might receive (otherwise, it might be tampered).

How do I process all that info on the server side?

I'll assume you use PHP (as this is a known language to me) but you can do it in whatever you want, the only condition is that it must be some http-friendly language. You'd get the sent variables this way:

<?php
  $latitude = $_POST['Latitude'];
  $longitude = $_POST['Longitude'];
  $username = $_POST['username'];
?>

And, for example, store it in a table with that 3 fields:

mysqli_query("INSERT INTO current_locations(longitude, latitude, username) VALUES($latitude, $longitude, '$username')", $my_link);

If you want to include the IP address of the user, you can get it with:

$ip = $_SERVER['REMOTE_ADDR'];

That's all. You're receiving your user's information and storing it in a database.

Great, now how do I spread all that info amongst my users?

You need some mechanism that would emulate a multicast protocol to send all that information. This case looks to suit perfectly to use Google Cloud Messaging. I've already written an answer a time ago that describes how to implement it, so I won't extend it here. Summarizing: You'll know who has registered into your Google Cloud Messaging project, and you can now send them the information you need. A few points to take in consideration:

  • You'll be bombarded by your users sending data from their location without any compassion. After a while, if you have enough users, you can be storing thousands of locations per minute. Somehow, you have to spread that info and delete the useless information.

  • In that approach, it seems that the best way to manage all that amount of data is using a synchronous method that would process all the current information in your database. If your server is a linux/unix environment, you might want to use cron for that. Under MS, a programmed task would be enough.

  • That said, you'd need to fire a script that would extract all the data and handle it accordingly, again, in your favorite language, and put it in that cron/programmed task to be fired every certain amount of time.

Hint 1: You'll probably want your app to be used worldwide. So if you're in Argentina, why would you want to send your location to an user that is in China? For that, you could implement some distance algorithm in that script that, for each of the locations, would calculate the closest users based on a distance treshold, and send that user's information just to them. One of the most known is the Haversine formula, but there are lots. Just find one that you consider appropriate and implement it. This will reduce pretty much the useless sent information, however, it will take more time to compute, so again your remote server's specifications are important. As an analogy, you won't be acting like a Hub, but as a Switch.

Hint 2: I've mentioned the amount of information stored. Each time you run this script, you should extract the whole set of data you have at that time, decide to whom send each location, and afterwards, remove it from the database. Once sent, this information will be obsolete, so why keep storing it and make your database huge and inefficient?

How do I display the received coordinate?

Google has an API for Google Maps, I recommend using it in your case. The relevant information to markers is here. If you read the refered link that I mentioned above about Google Cloud Messaging and the GCM documentation, you'll see that there's an IntentService that processes the messages that are sent to a specific client. So here, analogously to the code above about sending your location, now you're receiving a location. So you'll need to implement something like this:

public class GCMprocess extends IntentService {
  public GCMprocess() { super("GCMIntentService"); }

  @Override
  protected void onHandleIntent(final Intent intent) {
    final GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);

    final String messageType = gcm.getMessageType(intent);
    final Bundle extras = intent.getExtras();

    if ((!extras.isEmpty()) && (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType))) {
      // This method will be fired for each location of each user sent by your cron'ed script
      String longitude = extras.getString("longitude");
      String latitude = extras.getString("latitude");
      String username = extras.getString("username");

      // And this places the content in your google map:
      private GoogleMap mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
      mMap.addMarker(new MarkerOptions().position(new LatLng(Double.parseDouble(longitude), Double.parseDouble(latitude))).title("User " + username));
    }

    GCMBroadcastReceiver.completeWakefulIntent(intent);
  }
}
Community
  • 1
  • 1
nKn
  • 13,691
  • 9
  • 45
  • 62
  • Oh my god. Thats for sure the most amazing answer that i ever had here on StackOverflow. I am implementing this method and in little time i will give you a feedback, i have almost sure that no one will have a better answer than yours, so probably you will win the bounty :P – Paulo Roberto Rosa Apr 23 '14 at 12:41
  • 1
    Nah, there are lots of better answers than this, but as my theses was a bit about `GPS` locations, distances and patterns, I felt like a duck to water :-) Hope it helps. – nKn Apr 23 '14 at 13:03
  • Of course it helps :P you just saved my life, by the way, i have found some problems, first: you do not declared `response`, but its ok i know that is a `HttpResponse` object, and i have declared it, but you should edit your answer and declare it, second and more important problem: when i am sending the coordinates via HTTPPost i got `android.os.NetworkOnMainThreadException` what i need to do? – Paulo Roberto Rosa Apr 23 '14 at 18:44
  • Oops, yep, I copied that snippet from one of my projects and forgot to add the type. I updated my answer. For the second issue, you're probably running this code on the main UI (the main `Thread`). By default, `Android` forbids this as it would block the user interaction with your app, it would get irresponsive and you probably got an `ANR` (App Not Responding), which would lead to a very poor user experience. You need to put this in a separate Thread. The easiest way is implementing this within an `AsyncTask`. Example: http://stackoverflow.com/questions/9954477/async-http-post-android – nKn Apr 23 '14 at 19:00
  • Yay, i used one AsyncTask and now it is working, i did some tests to a .PHP file that writes a .txt file with the coordinates and they are coming :P i am starting to go into the Google Cloud Messaging part. – Paulo Roberto Rosa Apr 23 '14 at 19:35
  • Hey, i have found this GCM a little complicated, so i did the coordinates return this way: i have a nodeJS server and a get listener inside it, so on the android app i did a httpGet and the server returns me all the coordinates. But i found a problem: my AsyncTask does not execute the onPostExecute() method, do you know why? I have my Entity with all the coordinates inside it but after the `return EntityUtils.toString(entity);` the onPostExecute() do not work – Paulo Roberto Rosa Apr 24 '14 at 13:40
  • You can do the data sharing the way you think it's better, but keep in mind that now *you* (your server) are doing an additional hard work, and when you got thousands of coordinates to share, you'll notice its load. If you use `GCM`, it will be a Google's server who will do the dirty work. At first glance it might look messy, but once you understand how it works it's a wonder! Regarding to the `onPostExecute()` method, if you want, update your question or post some pastebin of your current code and I'll have a look. – nKn Apr 24 '14 at 15:24
  • You are saying to me that `GCM` can store my coordinates and username data and send it back to me itself? - regarding to the `onPostExecute()` i have found the problem, i wasnt `@Override`'ing the methods and the parameters was different so eclipse do not acused any error but after i put @Override it shows me that the parameters do not match and i changed them ^^ - i got my application working sending and getting all users coordinates and putting the mark for every one, by the way i am interested on what you just said to me about GCM – Paulo Roberto Rosa Apr 24 '14 at 15:59
  • No. In the `GCM` approach, you must have a remote server (which in your case is the same that processes the coordinates, right?) and you have also the `GCM` server. When a device installs your app, it subscribes to your `SENDER_ID` (which you get when you create a project), and the `GCM` server issues an unique `ID` for that device. That device then informs your server its `ID` (and possibly some other data, like IP, etc.), and you store that ID somewhere. Then, when you send your coordinates, along with them also send that `ID` so you can identify that user, and there's where the `GCM` system – nKn Apr 24 '14 at 17:20
  • joins the equation. Each time that you want to send someone a message (with coordinates of other users), you send a message to the `GCM` server with the `ID` of the user that you want to send that message, and `Google` will handle the delivery of that message, so even if the user is disconnected, it will mind to send that information once your user turns on the app again. So basically the `GCM` system is the "postman" of the information you want to send, and the users don't have to poll for coordinates doing requests to your server. – nKn Apr 24 '14 at 17:22
  • Humm.. it seems similar to the notifications that arrives when you get online ^^ i liked it, but tell me, how much do you think that GCM can improve on the performance? in a scale from 0 to 10? – Paulo Roberto Rosa Apr 24 '14 at 18:39
  • And another thing: GCM have a quota limit for applications? because i am planning to get huge number of accesses on mine app. – Paulo Roberto Rosa Apr 24 '14 at 18:44
  • More than performance, it will actually lift a big weight off, as if you decide implement delivery by yourself, you'll have to establish some kind of mechanism where your clients connect to your server (which brings more unnecesary load) and make the calculation of the close users on the fly, which might take several seconds. However, if you do it asynchronously and decide when to send the message, the client won't be waiting for a response, it will just get the `GCM` message when sent. There's no quota limit, just the limitation of `4KB` per message (more than enough for a coordinate pair). – nKn Apr 24 '14 at 19:45
  • Great, but a thing: `"the client won't be waiting for a response"` i dont know what are you trying to say, but that sounds bad for me, because i need to update on the map all the marker's(users locations) every 3~5 seconds on all the clients. – Paulo Roberto Rosa Apr 24 '14 at 20:02
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/51378/discussion-between-nkn-and-paulo-roberto) – nKn Apr 24 '14 at 20:31