I am implementing a solution to track location updates in my Application,both in foreground and background, and perform some task when location is updated.
As per the Google recommendations in https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderClient#requestLocationUpdates(com.google.android.gms.location.LocationRequest,%20android.app.PendingIntent), I used PendingIntent variant of requestLocationUpdates API.
What is the best way to perform a task when location is changed ?
As per the Google sample https://github.com/googlesamples/android-play-location/tree/master/LocationUpdatesPendingIntent, task to be performed when location changes, is done in the onReceive() of broadcast receiver. This approach doesn't deal with wakelocks and the device background restrictions.
As per my understanding after going through various stackoverflow answers and different blogs, I have to use JobIntentService.
Is using JobIntentService is the correct approach for my requirement
Do Application needs to acquire wakelock to perform a task when location changes, when device is in sleep mode(I am aware that JobIntentService automatically handles wakelocks).
When the system callbacks like onlocationChanged(),onReceive() of BroadcastReceiver() are invoked, is CPU awaked automatically when device is in sleepmode. If waked, how much time it will be active ? Does it wait for callback to be finished.
Found some useful info in Android source code comments
"When location callbacks are invoked, the system will hold a wakelock
on your application's behalf for some period of time, but not
indefinitely. If your application requires a long running wakelock
within the location callback, you should acquire it yourself."
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/location/java/android/location/LocationManager.java
Is using JobIntentService is the correct approach for my requirement
Using JobIntentService would be the correct approach,
1. It can handle wakelocks,
2. As there are background limitaions on Android "O" and above versions, it can work in Maitainance window of doze mode.
Do Application needs to acquire wakelock to perform a task when location changes, when device is in sleep mode(I am aware that JobIntentService automatically handles wakelocks).
Location change callbacks come with wakelocks acquired and loose if callback is returned. For any lengthy task in callbacks, Service has to be started in callback with wakelocks(JobIntentService would help here).
When the system callbacks like onlocationChanged(),onReceive() of BroadcastReceiver() are invoked, is CPU awaked automatically when device is in sleepmode. If waked, how much time it will be active ? Does it wait for callback to be finished.
System callbacks often come with wakelocks acquired, and release when callback is returned. As they run in UI main thread, any task to be done has to be offloaded to service. For services to run even in device sleep usecase, wakelocks have to be acquired, and again JonIntentService helps here.
JobIntentservice also deals with doze mode in a best possible manner.
Note: Because of the background location restrictions, to get the continuous location updates, App has to start the foreground service.
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.
In the book Pro Android 4 By Satya Komatineni , Dave MacLean I've found:
Android acquires a partial wake lock when invoking a broadcast service and releases it when it returns from the service in the main thread
Does it mean that the Android OS ensures that the device will wake up for the time of going through onReceive of BroadcastReceiver? In my case the BroadcastReceiver should get an intent from Google Play Services (GoogleLocationServices and to be precise Geofences api).
Where is it documented?
EDIT: I've also found it here:
because it is guaranteed that the BroadcastReceiver.onReceive() will be always fully executed before the CPU goes to sleep
There is no such thing as a "broadcast service".
Also, you need to read the entire post containing your second quote, as that is only for a broadcast triggered by AlarmManager. It is AlarmManager, not the broadcast Intent mechanism, that holds the WakeLock. Also, as noted in that blog post, Dianne Hackborn had confirmed this behavior, and she's a core Android engineer.
Does it mean that Adnroid OS ensures that the device will wake up for the time of going thru onReceive of BroadcastReceiver?
Not generally.
In my case the BroadcastReceiver should get intent from Google Play Services (GoogleLocationServices and to be precise Geofences api).
If the Google Play Services documentation does not make any claims regarding the behavior of your receiver with respect to wakefulness, you should assume that you are not inside of a WakeLock. If the work is something that will take 1-2ms, and therefore is probably safe to do in onReceive() anyway, you're welcome to take the risk and skip a WakeLock and hope for the best.
But usually a broadcast like this triggers more work, involving disk I/O and/or network I/O, and you need to get that work off of the main application thread. Frequently, you do that by delegating to an IntentService, as it gives you a background thread with a marker service to let the OS know that you're still doing some work here. And, to ensure that the device will stay awake for that work to complete, use WakefulBroadcastReceiver or my WakefulIntentService, to hold a WakeLock from early in onReceive() until the completion of the work in onHandleIntent().
Where is it documented?
AFAIK, it isn't. Get used to it, as for complex systems, usually only a tiny fraction of the system's behavior winds up being documented.
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 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.