How to keep a task alive after phone sleeps? - android

my application needs to send a message to my server every 30 seconds or so.
I understand I need to use AlarmManager using RTC_WAKEUP or ELAPSED_REALTIME_WAKEUP.
now I don't understand two things:
1) If the AlarmManager wakes up the device, why do I need to aquire a WakeLock?
2) I saw an example for using AlarmManager with WakeLock. In this example, its setting the alarm to send a broadcast to a broadcast receiver which then acquires a static wake lock and then start an IntentService which runs a task.
now, my question is, in my case, I need to follow this example entirely? why don't set the alarm to start a service instead?

Related

Why to put the WakeLock mechanism in BroadcastReceiver instead of having it in the Service?

I am having a look at the sample code from Google team for Android which is WakefulBroadcastReceiver
My question is is there a specific reason to have this mechanism acquire/release in BroadcastReceiver instead of putting this inside the Service itself. If yes what is it
?
It's very useful for something like alarms (see AlarmManager) or other types of PendingIntent use cases. With alarms send to BroadcastReceivers, the alarm manager mechanism ensures that the system will wake long enough to deliver the broadcast Intent (e.g. run the onReceive() method) to BroadcastReceivers only.
If you were to use a PendingIntent for a Service in this case, the Service would get "started" from an API perspective, but would not necessarily run because the system could go right back to sleep. Using a WakefulBroadcastReceiver, you could instead have the alarm trigger it, take the wake lock and start your Service. The Service would then get an opportunity to run and would ultimately need to release the wake lock so the system could go back to sleep.

Does AlarmManager require the PendingIntent to be of Type BroadcastReceiver?

The documentation for AlarmManager seems to imply (but does not outright explicitly require) that the PendingIntent you pass in to any of the set() methods should be of the type BroadcastReceiver, but I tested passing in other component types (like an IntentService) and it seemed to work fine.
Is it safe to use non-BroadcastReceiver Intents with AlarmManager?
Yes, and it has always worked, but I suspect not in the way that you're thinking. You can use any PendingIntent with an alarm; this could indeed be an activity or service PendingIntent. If it's a service PendingIntent, then the OS will call startService() for you when the alarm fires. The hidden catch is about the behavior of wakeup alarms.
When any alarm fires, the OS holds a wakelock on the sender's behalf for as long as it takes to deliver the PendingIntent, at which point the wakelock is released and the device is allowed to go back to sleep. The exact meaning of "as long as it takes to deliver" depends on which kind of PendingIntent is being used.
Broadcast delivery is essentially treated as synchronous: the wakelock is held by the Alarm Manager until the recipient's onReceive() callback returns. This gives you a hard guarantee that whatever processing you want to do in onReceive() is guaranteed to proceed without the device sleeping.
However, activity and service PendingIntent delivery does not wait for the recipient in the same way. With those kinds of alarm PendingIntents, the device remains awake long enough to begin the process of starting the target activity or service, but then it can (and does) go back to sleep immediately after that launch has begun, before the target code actually has a chance to run. In practice this means that with a service PendingIntent, even if the alarm is a wakeup alarm, the service will often not actually execute until the device as a whole is woken up normally, e.g. the next time the user turns on the screen manually.
Sometimes this is okay, if your code doesn't actually care that even though the alarm fired at 3am, the service didn't start running until 7am when the alarm clock went off and lit up the phone for an extended period. More often, though, what apps need to do is use a broadcast alarm, then in their onReceive() -- knowing that the device will sleep as soon as they return -- acquire their own wakelock and start up the service under that wakelock, etc.
There is a terrific support library class called WakefulBroadcastReceiver that encapsulates this alarm-wakelock-service dance and makes it both easy and bulletproof; it's https://developer.android.com/reference/android/support/v4/content/WakefulBroadcastReceiver.html. Use that if you ever want to start a service in response to a wakeup alarm.

Does periodic notifications work when device is in sleep

I am working with Android API especially Alarms, IntentService and notifications. I am using AlarmManager to schedule a periodic IntentService which might or might not fire notifications.
My questions is What happens when the device is in sleep mode?
Alarm will not fire and thus IntentService will not run at all. I am not sure if this will be the case.Will it make a difference if I make it a WakefulIntentService? I believe wake locks are needed to ensure the service keeps running after the BroadCastReciever returns. However, in this case there is no broadcast reciever.
Alarm and IntentService will run, but any notification will not have any impact since the device is sleeping. In this case, do I have to explicitly get a wakelock from PowerManager to fire notification ?
What happens when the device is in sleep mode?
That depends upon your type of alarm and the component your PendingIntent is to invoke.
If your alarm type ends in _WAKEUP, and you are using a broadcast PendingIntent, the device will wake up and remain awake through the call to onReceive() of the BroadcastReceiver. Once onReceive() returns, the device can fall asleep again. This is why WakefulIntentService and WakefulBroadcastRecevier were created -- to offer tested patterns for how to pass control to an IntentService and keep the device awake while the service completes its work.
If your alarm type ends in _WAKEUP and you are not using a broadcast PendingIntent, as the saying goes, your mileage may vary. You may not get control before the device falls back asleep. This is not a recommended pattern.
If your alarm types does not end in _WAKEUP, the device will not wake up due to your alarm.
With respect to the Notification, given the nature of the API, one hopes that it is the OS' responsibility to keep the device awake long enough for the ringtone or vibration pattern to play, as we do not know the precise instant when the Notification appears, nor do we know whether the ringtone will play (e.g., device is on silent mode).

Should an alarm be built with Service or BroadcastReceiver?

I want to build an alarm application. I've seen some examples and some of them use Service and some use BroadcasterReceiver. The user will set the alarm and then when it goes off they'll have to do certain things like solve a mathematical equation or scan NFC tag before it turns off. Which one should I use?
If you are using AlarmManager with a _WAKEUP alarm, you must have the PendingIntent route to a BroadcastReceiver. The only thing Android guarantees with a _WAKEUP alarm is if you use a BroadcastReceiver, Android will keep the device awake long enough for onReceive() to complete. Anything else, all bets are off.
It the work you want to do would take more than a couple of milliseconds, have the BroadcastReceiver turn around and pass control to a service, which can do its work on a background thread. You may wish to use my WakefulIntentService for that; if not, you will need to manage your own WakeLock to ensure that the device stays awake until the service can complete its work.

Should I use PendingIntent.getService() or getBroadcast with AlarmManager?

My app needs to grab some data from the web at a specific time each day. So I use an AlarmManager to schedule the task and that works ok.
But when looking at various examples there seems to be two ways to deal with the AlarmManager when it comes to the pending intent.
One uses PendingIntent.getBroadcast() to call a broadcast receiver when the alarm goes off and inside that receiver the service to do the real work is started.
Another approach is to use PendingIntent.getService() and call the service directly when that alarm goes off.
Can someone explain to me the difference between the two approaches so I can decide on which one to rely?
EDIT: One more question is where to acquire the wake lock when using getService()?
For example, when using a BroadcastReceiver I have the following line in onReceive():
WakeReminderIntentService.acquireStaticLock(context);
How should I acquire the wake lock if I instead call the service directly like:
PendingIntent pi = PendingIntent.getService(this, 0, new Intent(this, OnAlarmReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT);
Should I simply acquire it from within the service instead?
One uses PendingIntent.getBroadcast() to call a broadcast receiver when the alarm goes off and inside that receiver the service to do the real work is started.
it has one more step in starting service than
Another approach is to use PendingIntent.getService() and call the service directly when that alarm goes off.
then you should use the second approach as it is reducing your one step in execution..
Reading your edit I presume you found out yourself: If you want to make sure that your service is started when using AlarmManager, you better take the detour of first sending to a receiver and acquiring a wake lock there.
Otherwise it is possible that the phone will sleep before the requested service is launched.
That's what the javadoc of AlarmManager says and I also read it in post by Google engineer.
So now for your edit: When to acquire the lock?
The whole point of using the receiver is to acquire the lock within the onReceive() method of the receiver, because Android will not fall asleep during the execution of this method.
For an example see this question.

Categories

Resources