I have a background service which should track the user movement with his car, and send the data to my server. I have two variables for sending the location, either 60 seconds have passed or the user has moved 100 meters.
On my service here is how I start listening to locations:
mLocationRequest = LocationRequest.create();
mLocationRequest.setSmallestDisplacement(100);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(60 * 1000);
mLocationClient = new LocationClient(context, connectionCallbacks, onConnectionFailedListener);
mLocationClient.connect();
and the listeners:
ConnectionCallbacks connectionCallbacks = new ConnectionCallbacks() {
#Override
public void onConnected(Bundle arg0) {
mLocationClient.requestLocationUpdates(mLocationRequest, locationListenerAPI);
//process the last location, if available
Location loc = mLocationClient.getLastLocation();
if (loc != null) {
prepareLocation(loc);
}
}
#Override
public void onDisconnected() {
}
};
LocationListener locationListenerAPI= new LocationListener() {
#Override
public void onLocationChanged(Location location) {
//A location has been read, process it
prepareLocation(location);
}
};
So, while the service is running and I driver around with the car, onLocationChanged only fires a few times, it does not take into consideration either the time or meters to fire a location read. I have data connection and the GPS icon is visible on notification bar the whole time the service is running.
Any ideas why is not working ?
In the end, I just did it like this:
I've setInterval(5000) and setFastestInterval(5000) and then, for each read location, if distance between them is bigger than 100m, I send the location to my server. I gave up on using smallestDisplacement. This seem to work reliably.
Related
I have an AlarmManager that triggers every 30 minutes to an IntentService, and this intent service is to get the user's location each time. I have 2 ways of getting location: first it checks the getLastKnownLocation(), and if it was within the last 2 minutes it uses that, this part works perfectly.
The second way is if the last location was old or returns null, in which I want to get the new location once. For some reason this never calls onLocationChanged(). This results in my IntentService not returning coordinates much of the time, it only returns them if the getLastKnownLocation() was recent.
Here is my code for how it is set up, why is it that if I want to get a new location, it is never called? Check the comments in the code to see what is called and what is never called.
LocationListener locationListener;
LocationManager locationManager;
private final double MIN_COORD_DIFF = 0.0006;
public CoordinateAlarmReceiver(){
super("CoordinateAlarmReceiver");
}
#Override
protected void onHandleIntent(Intent intent) {
//THIS IS CALLED CORRECTLY
MyLog.i("coordinate alarm received");
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
Location lastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
//if last location was in past 2 minutes, use that
if(lastLocation != null && lastLocation.getTime() > Calendar.getInstance().getTimeInMillis() - 2 * 60 * 1000) {
//THIS IS CALLED CORRECTLY
storeLocation(lastLocation);
MyLog.i("Last location was recent, using that");
}
else { //otherwise get new location
//THIS IS CALLED CORRECTLY
MyLog.i("Last location was old, getting new location");
locationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
//THIS IS NEVER CALLED
MyLog.i("Got new coordinates");
storeLocation(location);
locationManager.removeUpdates(this);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
#Override
public void onProviderEnabled(String s) {
}
#Override
public void onProviderDisabled(String s) {
}
};
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
}
}
You should use a Service instead of a IntentService. The IntentService finish when complete the task however, the service is running and can listen the location change events. Try to use a service.
Hope it helps you!!
*Please have a look at the below written source code lines and suggest:-*
In the below mentioned code lines I am trying to request for GPS and Network location in every 60 seconds.
if (gps_enabled)
{
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 60000, 0, locationListenerGps);
}
if (network_enabled)
{
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 60000, 0,
locationListenerNetwork);
}
Below mentioned is the code for location change listeners:-
LocationListener locationListenerGps = new LocationListener()
{
public void onLocationChanged(Location location)
{
mCurrentGpsLocation = location;
String userCurrentGpsLocation = findUserAddress(mCurrentGpsLocation, mPreviousGpsLocation);
mPreviousGpsLocation = location;
for(int i = 0, size = mLocationUpdateListeners.size(); i<size; i++)
{
LocationUpdateListener listener = mLocationUpdateListeners.get(i);
listener.recieveGpsNotification(userCurrentGpsLocation);
}
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
LocationListener locationListenerNetwork = new LocationListener()
{
public void onLocationChanged(Location location)
{
mCurrentNetworkLocation = location;
String userCurrentNetworkLocation =findUserAddress(mCurrentNetworkLocation, mPreviousNetworkLocation);
mPreviousNetworkLocation = location;
for(int i = 0, size = mLocationUpdateListeners.size(); i<size; i++)
{
LocationUpdateListener listener = mLocationUpdateListeners.get(i);
listener.recieveNetworkNotification(userCurrentNetworkLocation);
}
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
And on receiving location change event for GPS and Network, I simply call my listeners recieveGpsNotification() and recieveNetworkNotification() methods respectively.
Logic of recieveGpsNotification() are as follows :-
public void recieveGpsNotification(String gpsLocation)
{
sendEmail();
}
**Problems :-
1) I expect call to sendMail() should come after every 60 sec but I am receiving notifications very frequently and after 4-5 notifications my application crashes. Please help if you see any error in implementation logic.
2) Will i also receive notifications for GPS even when my activity is in pause state or in stopped state, because i want to receive notification even when my activity is in background or it is stopped?
Additional Query :-
Whenever android framework provides us with GPS location updates, every time notification come through a different thread or is it the thread that request for notifications?
Thanks in Advance.
**
Try adding a min distance to each update. Also consider grouping the updates together for your sendEmail() method
Move your Location Listener logic into a Service that runs in the background. This will keep the updates coming. However, be aware that GPS updates are awful for battery life and users will not be happy to see GPS trying to get a lock all the time.
I believe those updates happen on the thread in which they are declared. So you should move your findUserAddress() method and other complex operations to a different thread.
Also, You should only use one method to get GPS updates. The normal use case is to try GPS Adapter, if not enabled or preset, fall through to use the Network listener. Since you have both you will be getting both updates on different intervals.
...or most probably, I am doing it wrong. What I want is to display a Toast every one meter I walk inside home. The code below gives me wrong results, as the moment I install the app on my phone I get a Toast without even moving!
public class MainActivity extends Activity {
private LocationListener mLocationListener;
private String mLocationProvider;
private LocationManager mLocationManager;
private Location mCurrentLocation;
private int mCounter = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
mLocationListener = new MyLocationListener();
Criteria criterion = new Criteria();
criterion.setAccuracy(Criteria.ACCURACY_FINE);
criterion.setCostAllowed(true);
criterion.setPowerRequirement(Criteria.POWER_HIGH);
mLocationProvider = mLocationManager.getBestProvider(criterion, true);
}
#Override
protected void onResume() {
super.onResume();
mCurrentLocation = mLocationManager.getLastKnownLocation(mLocationProvider);
mLocationManager.requestLocationUpdates(mLocationProvider, 5000, 1, mLocationListener);
}
#Override
protected void onPause() {
super.onPause();
mLocationManager.removeUpdates(mLocationListener);
}
private class MyLocationListener implements LocationListener {
#Override
public void onLocationChanged(Location newlocation) {
float distance = mCurrentLocation.distanceTo(newlocation);
if (distance >= 1) {
mCounter++;
Toast.makeText(MainActivity.this, String.format("Message #%d: you walked one more meter", mCounter), Toast.LENGTH_SHORT).show();
mCurrentLocation = newlocation;
}
}
}
}
A GPS signal is not precise enough to give exact locations for a 1m radius. There can be deviation peeks up to 50 - 100m in real situations using GPS. This depends much on the environment you are at. GPS will be reflected by buildings, water etc. An average deviation is 10 - 20m. This will get even worse if your inside of a building using a GPS provider instead of a Network provider.
Furthermore you will never get the same coordinates twice in a row, because of this. Even if you don't move! To avoid that you could temporarly save the location and compare it with the new location. If the distance between them hits a defined boarder use the new location.
Change your location provider to GPS. And you have instantiated the LocationListener before you request the new Location(in onResume(); onResume() will be called after onCreate()). This might be the reason for your app showing Toast on start up.. Try to instantiate LocationListener after the requestLocationUpdates()..
I have an Android app with a Google Map and automatic location updates via Google Play Services' new location API.
Implemented just like this:
https://developer.android.com/training/location/receive-location-updates.html
I'm specifically trying to receive GPS / accurate locations.
It works 100% perfectly and normally, GPS icon is on above, locations coming in every few seconds, no worries.
The odd problem seems to be that if you switch to Google Maps, wait a second, then switch back to my application, my app then gets exactly one more location update, and then stops receiving updates.
My app is properly stopping location updates onPause/onStop, and re-connecting and re-starting them onStart/onResume.
My debug Toasts show "Connected" after switching back from Google Maps, and show one more "Updated Location", then the updates stop. onDisconnected() is not getting called, and checks of mLocationClient.isConnected() report 'true'.
I have since added a hack work-around with a timer handler that runs every few seconds and if a location hasn't been found in the last 10 seconds, it calls stopPauseLocation() and checkStartLocation() below, which does fix the issue and locations start coming in again. Obviously this is an ugly hack and I'm not happy about it.
It seems to be like a bug, something conflicting between Google Maps and my own app, however, I can't for the life of me figure out a real solution.
Any ideas?
Here's the key code snippets:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create the LocationRequest object
mLocationRequest = LocationRequest.create();
// Use high accuracy
mLocationRequest.setPriority(
LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval to 2 seconds
mLocationRequest.setInterval(2000);
// Set the fastest update interval to 1 second
mLocationRequest.setFastestInterval(1000);
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
mLocationClient = new LocationClient(this, this, this);
mLocationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
}
#Override
protected void onStart() {
super.onStart();
// Check our connection to play services
checkStartLocation();
}
/*
* Called when the Activity is no longer visible at all.
* Stop updates and disconnect.
*/
#Override
protected void onStop() {
stopPauseLocation();
}
/*
* Called by Location Services when the request to connect the
* client finishes successfully. At this point, you can
* request the current location or start periodic updates
*/
#Override
public void onConnected(Bundle dataBundle) {
// Display the connection status
Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
mLocationClient.requestLocationUpdates(mLocationRequest, this);
super.onStop();
}
private void stopPauseLocation()
{
// If the client is connected
if (mLocationClient.isConnected()) {
/*
* Remove location updates for a listener.
* The current Activity is the listener, so
* the argument is "this".
*/
mLocationClient.removeLocationUpdates(this);
}
/*
* After disconnect() is called, the client is
* considered "dead".
*/
mLocationClient.disconnect();
}
/**
* Helper to check if we're connected to play, and try to connect if not
*/
protected void checkStartLocation() {
if (!mLocationClient.isConnected())
{
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationClient.connect();
}
}
/*
* Called by Location Services when the request to connect the
* client finishes successfully. At this point, you can
* request the current location or start periodic updates
*/
#Override
public void onConnected(Bundle dataBundle) {
// Display the connection status
Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
mLocationClient.requestLocationUpdates(mLocationRequest, this);
}
#Override
public void onDisconnected() {
// Display the connection status
Toast.makeText(this, "Disconnected.",Toast.LENGTH_SHORT).show();
}
/*
* Called by Location Services if the attempt to connect to
* Location Services fails.
*/
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Toast.makeText(this, "onConnectionFailed", Toast.LENGTH_SHORT).show();
}
// Define the callback method that receives location updates
#Override
public void onLocationChanged(Location location) {
// Report to the UI that the location was updated
String msg = "Updated Location: " +
Double.toString(location.getLatitude()) + "," +
Double.toString(location.getLongitude());
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
The only method I've found around this is a hack, but this is the best version of the hack I've come up with.
The code below will check every 10s (and then incrementally 15s, 20s, up to 30s) for a location update. If no location has been received, it calls removeLocationUpdates() and requestLocationUpdates(), which seems to fix my issue.
private Handler locationCheck = null;
// Track the time of the last location update
private long lastLocationUpdate = System.currentTimeMillis();
private long lastLocationWaitTime = 0;
// How long to wait until we reconnect (10 sec)
private long WAIT_LOCATION_AGE_START = 10000;
// Increments of how much longer to wait before next check
// Increments on every failure to give the system more time to recover
private long WAIT_LOCATION_AGE_INCREMENT = 5000;
// Max time to wait
private long MAX_WAIT_LOCATION_AGE = 30000;
Add to onCreate in your class:
locationCheck = new Handler();
Add to onResume/onStart:
lastLocationUpdate = System.currentTimeMillis();
lastLocationWaitTime = WAIT_LOCATION_AGE_START;
locationCheck.removeCallbacks(locationCheckRunnable);
locationCheck.postDelayed(locationCheckRunnable, 1000);
Add to onStop/onPause:
locationCheck.removeCallbacks(locationCheckRunnable);
private Runnable locationCheckRunnable = new Runnable() {
#Override
public void run() {
if ((System.currentTimeMillis() - lastLocationUpdate) > lastLocationWaitTime)
{
// Verify our connection
checkStartLocation();
// Reset the timer
lastLocationUpdate = System.currentTimeMillis();
// On next check wait a bit longer
lastLocationWaitTime += WAIT_LOCATION_AGE_INCREMENT;
lastLocationWaitTime = Math.min(lastLocationWaitTime, MAX_WAIT_LOCATION_AGE);
// Re-request location updates
mLocationClient.removeLocationUpdates(MyParentClassName.this);
mLocationClient.requestLocationUpdates(mLocationRequest, LocationSocialBaseScreen.this);
}
locationCheck.postDelayed(this, 1000);
}
};
#Override
public void onLocationChanged(Location location) {
lastLocationUpdate = System.currentTimeMillis();
}
I'm developing an application that needs to get location updates fast, doesn't matter the accuracy of them. I need to be able to get about one reading per second. How can I do this?
Other than specifying 0 for both the minimum distance and minimum time values in requestLocationUpdates(), you have no control over the rate. Android will give you all of the fixes it receives, but whether that is 30 fixes per second or 30 seconds per fix will depend upon hardware, environment (e.g., is the user indoors?), and so forth.
You could build a layer between the Android location updates and your receiver.
In your own layer, catch the Android location updates, and pass the same location 30 times per second to your receiver, until you get a new location.
EDIT
Something like this (not tested):
public class MyLocationManager implements LocationListener{
private List<MyLocationListener> listeners;
private Location lastLocation;
private Handler handler;
public MyLocationManager(){
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
lastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
listeners = new ArrayList<MyLocationListener>();
handler = new Handler();
sendLocationUpdates();
}
private void sendDelayedLocationUpdates(){
handler.postDelayed(locationUpdater, 200);
}
public void addMyLocationListener(MyLocationListener mListener){
listeners.add(mListener);
}
public void removeMyLocationListener(MyLocationListener mListener){
listeners.remove(mListener);
}
#Override
public void onLocationChanged(Location location) {
lastLocation = location;
}
public interface MyLocationListener{
public void onLocationChanged(Location location);
}
private Runnable locationUpdater = new Runnable(){
#Override
public void run(){
for(MyLocationListener mListener : listeners){
mListener.onLocationChanged(lastLocation);
}
sendDelayedLocationUpdates();
}
};
}