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
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:
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);
}
}