I'm working on an android app where I need to get location updates in the background for an extended period of time using the GPS provider, so I need the location udates to occur when the user is no longer interacting with it. I did some testing of my own comparing LocationManager's requestSingleUpdate (the provider, pendingIntent version) and requestLocationUpdates (the provider, minTime, minDistance, intent version). After taking a good bit of data on my Droid Razr HD, it looks like using requestLocationUpdates is significantly more reliable than requestSingleUpdate (for both methods I actually compared the GPS location reported to where I actually was, i.e. not using the reported accuracy, and requestLocationUpdates was not only significantly more accurate as to my actual location, but it returned a location fix time that was the same as the current time far more often then using the requestSingleUpdate option). I know how to use AlarmManager with a repeating alarm and call requestSingleUpdate so that I only need to hold a wakelock each time the the alarm goes off, but because of the increased accuracy of using requestLocationUpdates (at least in my tests on my phone) I would like to use that method instead. My problem is that if I use the requestLocationUpdates method, I am really unclear as to what I have to do in terms of holding a wakelock to keep the updates occuring when the user isn't interacting with the phone. I am calling requestLocationUpdates from a separate thread that is launched from a service (I read that that is the standard pattern). My question is, do I have to hold a wakelock for the entire time that I want location updates to keep coming? If I don't get my own wakelock do the location updates keep coming, waking the phone up for each new update? If this is true, do these updates create their own wakelock, and if so, when do they release the wakelock? I can't seem to find a clear answer on this. Any help is greatly appreciated.
No you don't need a wakelock because the OS takes the wakelock for you (code). The wakelock is released when onLocationChange() ends or when your broadcast receiver receives the intent (for the intent version of the interface). You need your own wakelock if you start some async work in onLocationChange() for example with an other thread.
I wrote a flight logging app which also needs update every few seconds to recognize take-oofs and landings. The received data is evaluated in a service. To keep the service alive you will need a WakeLock. I get the lock when the detector (automatically recognizing the relevant events) is started and I release the lock when the user turns the detector off; this is also the life time of the service, so the lock is aquired in onCreate and it is released in onDestroy. I use a SCREEN_DIM_WAKE_LOCK to save some power.
Related
My goal
Get user location updates for quite long time (e.g. 8 hours) with quite high frequency (e.g. every 30 seconds) even when the application is not running in foreground (meaning the activity where the location tracking was started might be destroyed).
Issue
I've found many articles regarding location tracking in Android apps.
The newer ones usually explain how to use Google Play Services' location APIs.
The problem is that in most cases, they demonstrate getting location updates in co-operation with Activity and LocationListener. For example in Google's tutorial. It's obvious this is not what I need. The only approach for long-running background location updates was based on periodical starting (via AlarmManager) of a service that run until it got accurate enough location update. However, this approach doesn't seem right for the frequency I need.
So, I ended up with custom idea how to solve the topic, but your critique would be welcomed. Here it is:
Idea of a possible solution
MainActivity - used just for starting/stopping the tracking by starting/stopping the MonitoringService
MonitoringService - a foreground service, where the whole connecting to LocationServices from Google Play services happens. Also, once connected a requestLocationUpdates method is called, but its variant with PendingIntent object. The pending intent contains intent invoking MyWakefulBroadcastReceiver class. Also, the service holds a partial wake_lock to prevent the device going to sleep and interrupting receiving of the location updates. Here, I'm not sure if holding the wake lock really helps.
MyWakefulBroadcastReceiver - extends WakefulBroadcastReceiver, just starts LocationProcessingIntentService via the startWakefulService
LocationProcessingIntentService - processes the location update passed via intent to it. Network communication performed here. Therefore, it is done in separate service and not directly in the MonitoringService
Currently, I have the solution described above implemented without acquiring the partial wake lock in the MonitoringService. When connected to debugger in Android Studio, I see the processing of the location updates work. However, I'm not sure what it will do after e.g. 4 hours of running without being connected to laptop (like it is for the debugging purposes)
Questions
Is this approach OK from architecture/performance/battery life point of view?
Should I use the wake lock for being sure the device won't go to sleep?
If answer to 2. is yes, do I still need to use WakefulBroadcastReceiver for starting the IntentService processing the location updates?
Any other recommendations?
What you have is mostly fine. You don't need the MonitoringService. The PendingIntent will wake your BroadcastReceiver, regardless of the state of your app. You don't need a permanent wake-lock. Just use the PendingIntent in your setup Activity to requestLocationUpdates.
Remember that the location services are already running in the background (as long as the user has enabled location services), so you don't need to run your permanent wake-lock service also (you can wake-lock after BroadcastReceiver#onReceive(), but don't forget to release the wake-lock after processing).
The only time you might have to worry about the PendingIntent not waking your BroadcastReceiver is if the user force-stops your app. In that case, you could choose to respect the user's decision. You can also explore using intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); which should wake your BroadcastReceiver from the stopped state, but I haven't tested this flag in this scenario.
My application requests for updates in a service on background when a boolean flag is set to true. If flag is set to true, then i acquire a PARTIAL_WAKE_LOCK to let my background service run.
My questions are:
Since I requested for updates from location manager ( i don't manually request updates but subscribed for locationManager.requestLocationUpdates )... does the locationManager keep working as normal/usual even if device goes to sleep with PARTIAL_WAKE_LOCK ?
I've read there is a WifiLock -> WIFI_MODE_SCAN_ONLY that I'm not acquiring. Since location manager uses wifi scans to detect location through wifi hotspots, should I acquire this as well ?
What about gps location updates when device goes to sleep ?
no, it does not. More (very good) info here, including possible solutions/ hacks.
regarding 1, I would have to make an educated guess and say it wouldn't make a difference
from what I can gather, it doesn't make a difference which provider you are using for the updates, LocationManager.NETWORK_PROVIDER or LocationManager.GPS_PROVIDER.
in danger of going a bit OQ, I am a bit curious which kind of application would need to aquire a wake lock to keep a service running at all. As far as I know, having a wake lock doesn't ensure your Service keeps running. The only thing which ensures a Service keeps running is to have it in the foreground (Service.startForeground()). Otherwise the system still might kill the service, regardless if it aquired a wake lock or not.
That being said, if it is running, it can do it's work with a Handler or something.
If you are using this approach, and I think you are based on the scenario, I would advise against it. Basically you are creating a service, have it run in the foreground (guess) AND you are aquiring a wake lock just to request for location updates when the screen is off. This seems a bit overkill.
There's a more efficient way, which has the benefit it has by far more accurate timing then the dreaded timing of Handler.postAtTime or Handler.postDelayed: AlarmManager.setRepeating(). Set type to ELAPSED_REALTIME_WAKEUP or RTC_WAKEUP so it will run if the device sleeps, then when the alarm event is fired and received by a BroadcastReceiver you will have to create, you could request for updates and handle other events.
If you're not using a Handler, but are merely requesting location updates, this approach still probably would be better, because it doesn't require you to have a running Service or to acquire a wake lock.
And it seems LocationManager.addProximityAlert() is the way to go here. Which is flawed as well (see 1)
Similar question here by the way: Android: GPS location updates when the cellphone is in sleep?
I've spent days trying to get WiFi and cell-based locations with locked screen with Android 6.0 on Nexus 6. And looks like the native android location service simple does not allow to do it. Once device got locked it still collects location update events for 10-15 minutes then stops to providing any of location updates.
In my case the solution was to switch from native Android location service to Google Play Services wrapper called com.google.android.gms.location: https://developers.google.com/android/reference/com/google/android/gms/location/package-summary
Yes, I know that some of Android devices lack of GMS, but for my application this is the only solution to perform.
It does not stop sending location updates even when in the background and device screen is locked.
Me personally prefer RxJava library to wrap this service into a stream (examples included): https://github.com/mcharmas/Android-ReactiveLocation
I developing an app which tracks a user via GPS and reminds them if they cross a toll bridge.
I obviously need the GPS location listener to run in a service and I'll also need a partial-wakelock so it can run occasionaly when the phone is asleep.
I also want the GPS updates to vary in frequency depending on the distance from the toll bridge to save battery.
The cwac- WakefulIntent service seems ideal for what I'm trying to achieve.
However, there are a couple of problems I can see me having before I head down this route (if you pardon the pun ;-).
Does the WakefulIntent service exit and release the wakelock once doWakefulWork() completes even if I'm waiting for my locationlistener to return some GPS updates.
How can I prevent doWakefulWork for returning until I get a location update and cleanup my listener.
What happens if I'm still waiting for a GPS update when alarm manager starts the service again, i.e. before doWakefulWork() has completed?
How can I persist data between instanciations of the service. Can I stuff an array of GPS co-ords into SharedPrefs?
Finally, as I get closer to a toll bridge I need more GPS frequent updates. Do I manage that within doWakefulWork() or by altering scheduleAlarms() so that it uses setRepeating() with a number of minutes stored in SharedPrefs by the service. The idea here is to throttle GPS usage based on proximity to an area of interest.
While the demo app provides a template to work from, I haven't been able to find any solid examples of WakefulIntentService doing any asynchronous jobs.
The cwac- WakefulIntent service seems ideal for what I'm trying to achieve.
Not really. IntentService is not good for location tracking, because you cannot register a listener. The service will shut down once onHandleIntent() ends.
Personally, I would use addProxmityAlert() on LocationManager, rather than mess with any of this yourself.
Does the WakefulIntent service exit and release the wakelock once doWakefulWork() completes even if I'm waiting for my locationlistener to return some GPS updates.
Yes.
How can I prevent doWakefulWork for returning until I get a location update and cleanup my listener.
You don't. You use something else, such as LocationPoller, or, better yet, addProximityAlert().
What happens if I'm still waiting for a GPS update when alarm manager starts the service again, i.e. before doWakefulWork() has completed?
You ensure that you have appropriate timeout logic in place to prevent this, such as can be found in LocationPoller.
How can I persist data between instanciations of the service. Can I stuff an array of GPS co-ords into SharedPrefs?
Yes, or a database, or a file in a format of your choosing.
Finally, as I get closer to a toll bridge I need more GPS frequent updates. Do I manage that within doWakefulWork() or by altering scheduleAlarms() so that it uses setRepeating() with a number of minutes stored in SharedPrefs by the service.
You would change your alarm schedule.
I haven't been able to find any solid examples of WakefulIntentService doing any asynchronous jobs.
WakefulIntentService is the "asynchronous job". It does not execute other asynchronous jobs.
I have a service (with a wakelock) that must run continuously behind the scenes capturing user Geo Location. The Service implements the LocationListener methods (i.e. onLocationChanged()).
However it takes some time for onLocationChanged() to get invoked by the phone, so in the meantime my service has to do something. I thought of using Thread.sleep(), but will that prevent the phone from invoking onLocationChanged()? Or should I do polling: while(i < 1000,000) {++i;}?
I'm not getting such abundant GPS results using either of those ideas; wondering if anybody can give me a tip on how to accomplish this.
I think you can use wait(), and notify() with synchronize block with LocationListener instance.
search for samples using wait(), notify().
I guess you want to keep the service "alive" while it is waiting for location changed information. That is taken care of by the system and you do not have to add code for that. When location change information becomes available, the onLocationChanged() would be invoked in the context of your service.
I solved the problem by removing WakeLock (waste of battery), doing a 10-second busy wait/Thread Sleep (just in case one blocks the GPS invocation of onLoc()), and using AlarmManager to wake the device from sleep and start the service.
This: gets GPS during sleep AND doesn't drain battery power.
I set an alarm with the flag RTC_WAKEUP to run a IntentService every 30 seconds to transmit location updates to a server. I'm planning to change the flag to RTC so it won't wake up the phone and just run when another process wake ups the phone. If I leave a LocationListener registered, will it still listen for location updates while the phone is asleep?
Yes - working location service has it's own wake lock. However better approach is manually set proper wake lock in your broadcast receiver. Please consider some optimization - sending data over network every 30s will drain battery.
You have multiple problems here.
I set an alarm with the flag RTC_WAKEUP to run a IntentService every 30 seconds to transmit location updates to a server.
First, you may not even get your first fix within 30 seconds, particularly if you are using GPS. You need to take into account that you may never get a fix (e.g., the user is in an underground location).
Second, please allow this figure to be user-configurable, including an option for "I'll upload the data manually please". As #piotrpo indicates, this is a significant drain on the battery. In fact, if you're using GPS, I doubt the battery will last more than a couple of hours.
Third, an IntentService will not work well in this case, because the IntentService will shut down before your fix arrives. At best, you'll leak memory. At worst, you won't get your fix, because Android terminates your process.
A better solution for doing background location checks is to use a regular Service, not an IntentService. The regular Service would register the LocationListener in onStartCommand(), plus arrange for a timeout notification (e.g., AlarmManager and set()) in case a fix is not available. When the fix arrives, run an AsyncTask to do your upload. When the AsyncTask completes, or if the timeout arrives and you did not get a fix, unregister the listener and call stopSelf() to shut down the service. Along the way, you will need to maintain your own WakeLock, to keep the device awake while all of this is going on.
For an example of most of this (minus the server upload part), see my LocationPoller.
If you are dead-set on this occurring every 30 seconds or so, you may as well not bother with AlarmManager at all. You would have to have an everlasting service, running all the time, with a permanent WakeLock and a permanent LocationListener. When fixes arrive in onLocationChanged(), upload them if they are more than 30 seconds from the previous one. And, be sure to wear a flame-retardant suit when you release the app, as those who run it may not like the results much.