I am experiencing difficulties reliably throttling the gps update rate on the fly. The following approach seems consistent with everything I read, and it will occasionally change the update rate once or twice (to, say, taking one GPS reading every four seconds) but after that it just stays at a rate and will no longer change.
private LocationManager _locationMgr;
private LocationListener _locationListener;
private int _secondsPerUpdate=-1;
// Constructor
public AshGps(Activity l_activity, int l_secondsPerUpdate)
{
_locationMgr = (LocationManager) l_activity.getSystemService(Context.LOCATION_SERVICE);
_locationListener = new mylocationlistener();
_locationMgr.requestLocationUpdates(LocationManager.GPS_PROVIDER, l_secondsPerUpdate*1000, 0, _locationListener);
_secondsPerUpdate = l_secondsPerUpdate;
}
// Called up to once every three seconds
// to change the update rate
void ChangeUpdateRate(int l_secondsPerUpdate )
{
if( _secondsPerUpdate != l_secondsPerUpdate )
{
_locationMgr.removeUpdates(_locationListener);
_locationMgr.requestLocationUpdates(LocationManager.GPS_PROVIDER, l_secondsPerUpdate*1000, 0, _locationListener);
_secondsPerUpdate = l_secondsPerUpdate;
}
}
// Methods handles the incoming GPS reading 'event'
private class mylocationlistener implements LocationListener
{
#Override
public void onLocationChanged(Location location)
{
...
The time period per update is a suggestion to Android. It will not always be honored. Quoting the documentation:
This field is only used as a hint to conserve power, and actual time between location updates may be greater or lesser than this value.
Related
I'm trying to find a way to turn off the GPS immidietly in case a good enough location was found, while still having a time limit to "give up".
I tried to do this with the following strategy:
start checking for locations, as soon as a location that has an accuracy lower than the maximum tolerated, pass it to the next function for processing and stop looking for updates.
Also, to save battery life, if such location could not be found in 30 seconds, stop looking for location updates without passing a value (basically give up, and hope to better luck next time).
To count the 30 seconds, I'm using a handler. But as soon as I write the line locationManager.removeUpdates(locationListener); in the handler, the locationListener in the parenteses in both lines (the one in the handler and the one in the listener) turns red and reports an error: The local variable locationListener may not have been initialized
Here is my code:
private void checkProximity() {
final LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
//start tracking location
final LocationListener locationListener = new LocationListener() {
...
#Override
public void onLocationChanged(Location location) {
//if new accuracy is better than the best estimate - update the best estimate
if(location.getAccuracy() < MAXIMUM_TOLERATED_ACCURACY) {
//forward location to scanProximity and end the location search
scanProximity(location);
locationManager.removeUpdates(locationListener); //FIRST LINE (see below)
}
}
};
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
500, 0, locationListener);
Handler h = new Handler();
int delay = 30 * SECOND;
Runnable removeListener = new Runnable() {
#Override
public void run() {
//if this code is reached - the maximum tolerated accuracy was not met in the period time
//extended to find a location
//TODO stop the location manager and return without forwarding a value
locationManager.removeUpdates(locationListener); //as soon as I write this line, the FIRST LINE and this line turns red.
}
};
h.postDelayed(removeListener, delay);
}
Is there anyway I can do this differently so I won't get an error?
I recommend you use Little Fluffy Location Library to work with GPS locations. Check out the examples codes and see which makes you more easy the solution to your problem , this is a beautiful way.
...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'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();
}
};
}
I have been working for 2 days looking for this bug. I have searched stackoverflow and Android documentation with no luck.
My onLocationChanged code has a counter in it that counts how many times it has been called and if I back arrow out of the activity screen on the phone and return, the counter will go up by 2 for each update. If I back arrowing out and update the GPS, the counter records that onLocationChanged is still getting called even though the screen is in the background and onPause has been called. If I go in and out of the activity with the backarrow, I can get more than two updates per GPS input send depending on how many times the activity screen is entered.
All the GPS code works but these multiple instances can't be good and they mess up other things I am trying to do, like distance traveled between two updates.
Here is what I think is the relevant parts of my code. Obviously I left out the main part but the point is that after returning to this screen after back-arrowing out then a single send of a GPS data point increments the n variable by more than one depending on how many times I have returned to the screen.
I must be doing something obvious but I can't find it.
public class Data extends Activity {
protected LocationListener ll;
protected LocationManager lm;
static int n = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.data);
LocationManager lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
LocationListener ll = new mylocationlistener();
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, ll);
}
class mylocationlistener implements LocationListener{
#Override
public void onLocationChanged(Location location) {
if (location != null){
n = n + 1;
textData.setText("\n" + n);
}
}
and
#Override
protected void onPause() {
if(lm != null) {
lm.removeUpdates(ll);
}
ll = null;
lm = null;
super.onPause();
}
The only clue I have is that if I take the lm.removeUpdates(ll) out of the if(lm != null) then the code crashes which makes me think that lm must be null and that lm.removeUpdates(ll) must not be correct but it matches the all the examples I could find as well as the Android documentation as far as I can tell.
Please help.
LocationListener ll = new mylocationlistener();
This LocationListener is local to your method onCreate().So is your LocationManager lm.So when you are removing updates its not working with the manager and listener that you declared as the class variable.
Just write as
lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
ll = new mylocationlistener(); in your onCreate().
I've set up the LocationManager to get the current location every 2 minutes:
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 120000, 0, this);
This works fine and onLocationChanged is called every 2 minutes as expected. However, it seems that it is being called multiple times over a 10-40 (a random amount) second span every 2 minutes. I log every location received in the onLocationChanged so here are some samples to get an idea of what is going on:
At 17:30
GPS 32.0 50.66318929195404 10.735434293746948 0.0 2010.08.07 17:30:10
GPS 32.0 50.66315710544586 10.735423564910889 0.0 2010.08.07 17:30:14
GPS 32.0 50.66314101219177 10.735418200492859 0.0 2010.08.07 17:30:17
GPS 32.0 50.66314101219177 10.735418200492859 0.0 2010.08.07 17:30:20
GPS 24.0 50.66313564777374 10.735418200492859 0.5 2010.08.07 17:30:24
GPS 32.0 50.663098096847534 10.735573768615723 0.0 2010.08.07 17:30:28
GPS 32.0 50.663065910339355 10.735611319541931 0.0 2010.08.07 17:30:31
Then I get no more updates for 2 minutes.
At 17:32
GPS 32.0 50.661821365356445 10.737022161483765 1.0 2010.08.07 17:32:39
GPS 16.0 50.66170871257782 10.737043619155884 1.8200275 2010.08.07 17:32:45
GPS 24.0 50.661579966545105 10.737027525901794 1.25 2010.08.07 17:32:50
GPS 16.0 50.66150486469269 10.73712408542633 1.0 2010.08.07 17:32:55
GPS 12.0 50.661579966545105 10.73715090751648 0.9013878 2010.08.07 17:33:01
GPS 24.0 50.66139221191406 10.737038254737854 1.5811388 2010.08.07 17:33:06
GPS 16.0 50.66141366958618 10.737301111221313 0.70710677 2010.08.07 17:33:12
GPS 16.0 50.66141366958618 10.737301111221313 0.70710677 2010.08.07 17:33:12
GPS 24.0 50.661311745643616 10.737070441246033 1.118034 2010.08.07 17:33:16
GPS 16.0 50.66122591495514 10.737177729606628 1.118034 2010.08.07 17:33:22
GPS 12.0 50.66124200820923 10.737220644950867 1.3462912 2010.08.07 17:33:26
GPS 12.0 50.661311745643616 10.737268924713135 3.6055512 2010.08.07 17:33:25
And so on... then another set of updates 2 minutes later at 17:35.
Is this the standard behavior? I was expecting to get only one location every 2 minutes, and the timespan in which it gives me location updates seems rather random. Ideally I would prefer to only get one location... is there a way to do this?
From the documentation of requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener) concering the minTime parameter:
"the minimum time interval for notifications, in milliseconds. This field is only used as a hint to conserve power, and actual time between location updates may be greater or lesser than this value."
So the answer to your questions, yes this is standard behavior and, no you cannot change this.
If this is a problem for you, you could ignore calls to the callback method if a certain amount of time hasn't passed.
I found this question because I had the same problem.
I believe I have the answer. The rapid updates are being fired because you have the meters parameter set to 0.
Change the meters parameter to something like 10 and it will only fire the LocationChanged event every 2 minutes IF your location changed by 10 or more.
Before I made this change, LocationChanged was firing multiple times a second. Now, it fires once. Then every 2 minutes, you will see the GPS icon on the status bar, but unless your location changed, the event doesn't fire.
I hope this helps. This is what fixed it for me. Didn't have to add any extra logic to prevent false fires.
this is my LocationListener implementation to filter out unnecessary onLocationChanged() events:
NOTE I use messages in my service.
public class GPSlocationListener implements LocationListener
{
//member variables
private Handler mParentHandler;//points to Handler of parent
private long mTimeBetweenLocationEvents;
private long mTimeOfLastLocationEvent;
private boolean mAccuracyOverride;
private float mLastAccuracy;
private boolean mOverrideLocation;
//constants
private static final float INVALID_ACCURACY = 999999.0f;
private static final String TAG = "GPSlocationListener";
//constructor
public GPSlocationListener(Handler parentMsgHandler, long timeBetweenLocationEvents, boolean accuracyOverride)
{
mParentHandler = parentMsgHandler;
mTimeOfLastLocationEvent = 0;
mAccuracyOverride = accuracyOverride;
mLastAccuracy = INVALID_ACCURACY;
mOverrideLocation = false;
mTimeBetweenLocationEvents = timeBetweenLocationEvents;
}
//EVENT: onLocationChanged()
// send GPS coordinates to CommService
public void onLocationChanged(Location loc)
{
Log.d(TAG, "onLocationChanged() triggered. Accuracy = "+Float.toString(loc.getAccuracy()));
mOverrideLocation = false;
if (loc != null)
{
//if a more accurate coordinate is available within a set of events, then use it (if enabled by programmer)
if (mAccuracyOverride == true)
{
//whenever the expected time period is reached invalidate the last known accuracy
// so that we don't just receive better and better accuracy and eventually risk receiving
// only minimal locations
if (loc.getTime() - mTimeOfLastLocationEvent >= mTimeBetweenLocationEvents)
{
mLastAccuracy = INVALID_ACCURACY;
}
if (loc.hasAccuracy())
{
final float fCurrentAccuracy = loc.getAccuracy();
//the '<' is important here to filter out equal accuracies !
if ((fCurrentAccuracy != 0.0f) && (fCurrentAccuracy < mLastAccuracy))
{
mOverrideLocation = true;
mLastAccuracy = fCurrentAccuracy;
}
}
}
//ensure that we don't get a lot of events
// or if enabled, only get more accurate events within mTimeBetweenLocationEvents
if ( (loc.getTime() - mTimeOfLastLocationEvent >= mTimeBetweenLocationEvents)
||(mOverrideLocation == true) )
{
//be sure to store the time of receiving this event !
mTimeOfLastLocationEvent = loc.getTime();
//send message to parent containing the location object
Message msgToMain = mParentHandler.obtainMessage();
msgToMain.what = Constants.MSG_LOCATION_CHANGED;
msgToMain.obj = loc;
mParentHandler.sendMessage(msgToMain);
}
}
}
public void onProviderDisabled(String provider)
{
// TODO Auto-generated method stub
}
public void onProviderEnabled(String provider)
{
// TODO Auto-generated method stub
}
public void onStatusChanged(String provider, int status, Bundle extras)
{
// TODO Auto-generated method stub
}
}
this is implemented as follows in your main code:
//create the APIthread (which exposes mAPIhandler back to this service)
mAPIthread = new APIthread(mApiHandler);
mAPIthread.start();
//use the LocationManager class to obtain GPS locations
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationListener = new GPSlocationListener(mApiHandler, Constants.LOCATION_UPDATE_PERIOD_MSEC, true);
//this will call GPSlocationListener.onLocationChanged()
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
//3 mins NOTE: approximate value
Constants.LOCATION_UPDATE_PERIOD_MSEC,
//no distance updates desired
0,
locationListener);