Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: requestLocationUpdates updates location at most every 45 seconds

Tags:

Background

I am writing an Android app whose main function is tracking the user's location and making an alert when the user gets near some point. Therefore I need to update the user's location at regular intervals, and these intervals should get smaller as the user comes closer to the target. So when the user is within, say, 1 km of the target, I want the location to be updated every 20 seconds and so on, until the user arrives.

Problem

When I test it (provider = LocationManager.NETWORK_PROVIDER), a call to requestLocationUpdates(provider, minTime, minDistance, locationListener) with any minTime < 45000 has the same effect as minTime = 45000, i.e. I get updates with an interval of exactly 45 seconds.
I know the minimum time parameter is only a "hint", but it is not taken as a hint by my app. I get updates with the interval specified until that interval passes below 45 seconds. It seems as though a minimum time of 45 seconds between location updates is hardcoded into Android, but that would be kind of odd. Plus I have never heard of this problem before, and I have not been able to find it addressed here on Stackoverflow.

Because I am not able to get frequent updates, my workaround (for now) is to manually call requestLocationUpdates whenever a new location is needed, and then just use the first available location. To do this at small intervals I use handler.postDelayed(myRunnable, updateInterval) to delay the calls, and myRunnable then takes care of calling requestLocationUpdates. However, this method only works about 50 (apparently random) percent of the time.

Does anybody know of the problem, and is there a way to fix it? Or is my only option to set minTime = 0 and just hope for the best?

Source code

Here is the source code for myRunnable, whose run() method I manually call regularly with handler.postDelayed(myRunnable, updateInterval):

public class MyRunnable implements Runnable {     private LocationManager manager;     private LocationListener listener;      @Override     public void run() {         // This is called everytime a new update is requested         // so that only one request is running at a time.         removeUpdates();          manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);          listener = new LocationListener() {             @Override             public void onLocationChanged(Location loc) {                 location = loc;                 latitude = loc.getLatitude();                 longitude = loc.getLongitude();                 accuracy = Math.round(loc.getAccuracy());                  handler.sendMessage(Message.obtain(handler, KEY_MESSAGE_LOCATION_CHANGED));                  checkForArrival();             }              // Other overrides are empty.         };          if(!arrived)             manager.requestLocationUpdates(provider, updateInterval, 0, listener);     }      /**      * Removes location updates from the LocationListener.      */     public void removeUpdates() {         if(!(manager == null || listener == null))             manager.removeUpdates(listener);     }      // Another method for "cleaning up" when the user has arrived. } 


And here is my handler:

handler = new Handler() {         @Override         public void handleMessage(Message msg) {             switch(msg.what) {             case KEY_MESSAGE_LOCATION_CHANGED:                 if(myRunnable != null) {                     myRunnable.removeUpdates();                     handler.postDelayed(myRunnable, updateInterval);                 }                 break;             }         }     }; 


Additional info

The whole location updating thing runs in a service.

I have read the doc several times, Google'd the problem, and tried various other workarounds. Nothing quite does it.

I have logged the damn out of this thing, and the only exciting thing to see is a big fat "ignore" to my frequent location requests. All the right methods are called.

Any help will be very much appreciated!

like image 601
stemadsen Avatar asked Feb 29 '12 22:02

stemadsen


People also ask

What is Android location manager?

android.location.LocationManager. This class provides access to the system location services. These services allow applications to obtain periodic updates of the device's geographical location, or to be notified when the device enters the proximity of a given geographical location.


1 Answers

You are completely right, the minimum time 45 seconds is harcoded in Android.

This seems to be a NetworkLocationProvider class source code, when it was still in Android core:

http://www.netmite.com/android/mydroid/frameworks/base/location/java/com/android/internal/location/NetworkLocationProvider.java

Look at the variable:

private static final long MIN_TIME_BETWEEN_WIFI_REPORTS = 45 * 1000; // 45 seconds 

And the method:

@Override public void setMinTime(long minTime) {     if (minTime < MIN_TIME_BETWEEN_WIFI_REPORTS) {         mWifiScanFrequency = MIN_TIME_BETWEEN_WIFI_REPORTS;     } else {         mWifiScanFrequency = minTime;     }     super.setMinTime(minTime); } 

Now NetworkLocationProvider is out of the Android core, you can find it in NetworkLocation.apk in /system/app

You can find an explanation of why is out of the core here:

https://groups.google.com/forum/?fromgroups=#!topic/android-platform/10Yr0r2myGA

But 45 seconds min time seems to still be there. Look at this NetworkProvider decompilation:

http://android.fjfalcon.com/xt720/miui-trans/apk-decompiled/NetworkLocation/smali/com/google/android/location/NetworkLocationProvider.smali

.line 149 const-wide/32 v4, 0xafc8  iput-wide v4, p0, Lcom/google/android/location/NetworkLocationProvider;->mWifiScanFrequency:J 

As you might guess if you convert 0xafc8 to decimal you get 45000 milliseconds

I haven't found an explanation of why 45 seconds. I suppose there will be reasons like avoiding service overloading or other uses they don't want.

In fact, there is a 100 request courtesy limit to Geolocation API:

https://developers.google.com/maps/documentation/business/geolocation/#usage_limits

But they don't seem to respect this rule in Google Maps app. If you open it and you only active network location you can notice that yout location is updated much more frequently than 45 seconds.

I noticed this line suspiciously frequent (33 times a second) in logcat when Google Maps is open:

02-20 17:12:08.204: V/LocationManagerService(1733): getAllProviders

I guess Google Maps is also calling removeUpdates() and requestLocationUpdates() again to obtain a new position.

So I think there is no fix and this is the best you can do if you want to get network locations over one in 45 seconds.

like image 185
djpeinado Avatar answered Oct 19 '22 23:10

djpeinado