Android background Services, Alarms and preserving object reference after application restart - android

I'm developing a little Android app, that needs to run a background process, used to start a remote connection periodically (for example, to check if there is new data on the server). This process obviously needs to work also if the application activity is not running at the moment.
As I can see in documentation, there are two types of approach to develop a scheduled background process in Android, working also when the application is closed.
Services
Alarms
The first one is not so good for my requirementes, because it can be killed by OS in case of low memory, so it is useless for me. startForeground() is not so good because I want the process is silent.
Alarm is ok, because it can't be killed by the OS, so it can work indefinitely. But... If I schedule an Intent with the AlarmManager, how can I preserve a reference to the Intent, surviving at application restart?
For example, if I want to cancel, or reschedule the Alarm, I need the reference to the initial Intent to cancel it thorugh the "AlarmManager.cancel(Intent i)" method. But if the application was restarted by the user, how can I obtain a reference to the initial Intent that was used to start the alarm?
Is there another way to stop an alarm if the launching application was restarted?

about alarms, you can cancel using the intent characteristics, so you don't need a reference to the original intent. In any case, the alarms mechanism still need you to run something on a service.
In any case, you missed another possible solution: SyncAdapter. Its purpose is to sync with servers, but you can do whatever you wish in the code, and it's unlikely the OS will kill it, as opposed to the other solutions you've mentioned.
Sadly even now it lacks on documentation and samples, but I think it can fulfill your needs. Here's what I've found

yes, note that cancel is looking for a PendingIntent, not an Intent per se.
so
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, R.string.some_string, new Intent(this, InitialIntent.class), 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
gets you where you want to be.
note that I'm not getString'ing that string, that's becasue I want a unique Id/request code for this intent that I'm not going to screw up copypasting; I reference the same number when creating the alarm in InititalIntent.

This question was just answered, here:
How can I get the context of other activity?
Just cancel the intent you scheduled

Your best bet would be to make use to push notification (via GCM), whenever something new is available on the server. I am working on similar application where data can be pushed from one side (producers) and needs to be pushed to the consumer apps.
You need to look at BroadcastReceiver and GCM specific communication model. Hope this helps

Related

Scheduling background work in Android

I'm new to Android so I want to make sure that the following solution is the correct one.
The problem:
I want to sync the device's local database with a database on my server, via a webservice, every 10 minutes. I already have a web service call that I can make that returns the new/updated records. What I'm wondering is what is the best way to schedule this task. I want the databases to sync even when the application is not running.
My solution (is this the correct route to go?):
I will have one BroadcastReceiver that listens for android.intent.action.BOOT_COMPLETED, in it's onReceive I will create an AlarmManager that sends a message to MyReceiver (via a PendingIntent) every 10 minutes. Also, in my application's startup I will do the same (create an alarm to send messages to the MyReceiver via a PendingIntent) - Since both alarms are sending messages to MyReceiver and their corresponding PendingIntents are initialized with PendingIntent.FLAG_UPDATE_CURRENT will the new alarm override the old one? (this is what I want to do, in case for some reason the alarm gets cancelled after device boot it should be restarted when the application starts).
In MyReceiver's onReceive() I will create a MyIntentService (this instance will make the webservice call and update the local database).
Is this a good solution? Any suggestions?
Thanks
Solution is fine...Actually all the AlarmManager instances get cleared when device turned off and rebooted.
The simple way is that...
First create AlarmManager when application started.
Second in onReceive of BOOT_COMPLETED BroadcastReceiver.
Its enough, PendingIntent.FLAG_UPDATE_CURRENT will make sure of having only one activated alarm at a time.
In this way, alarm registered when your application started. There will be no issue if its already registered via BOOT_COMPLETED. Activated alarm will deactivated when you turn off your device, but BroadcastReceiver to BOOT_COMPLETED will take care of registration new alarm at next boot.
If you decide that this answers your question, please mark it as "accepted". This will raise both your and my reputation score.
Also you need to review your interval to use network, it might be very resource consuming for device and user. One policy might be to have longer period of interval and check for update when user starts your app (this might not be user friendly but can save many system resources and battery power as well). Try to find some better policy according to your needs.
Using FLAG_UPDATE_CURRENT in that manner will override the existing PendingIntent if one exists. I'm not positive but I believe that as soon as you get into onReceive, the PendingIntent is consumed so it's no longer there to be overridden. In either case, it sounds like this is the functionality you are looking for and yes it's a good way to solve this kind of problem. My only other suggestion would be if the 10 minute interval timing is not absolutely critical then use one of the INTERVAL_ schedules (INTERVAL_FIFTEEN_MINUTES for example) in your AlarmManager to help conserve battery life; basically it lets allows all apps that run on intervals to "batch" their work together and wake the device up less frequently.

How to create a persistent alarm to notify user of events?

I currently have an AlarmManager with BroadcastReceiver implementation as my Alarm. There is one major flaw with this though; it does not go off when force closing the app. I tested out what happens with ICS calendar app, and it goes off even if I force close it. I know some people will say "if the user closes the app in that manner, they do not want it going off." What about task killers? That is the case I am looking for. Its pretty obvious my method cannot accomplish this, and I have looked and looked, but most if not all the examples are like how I implemented it. Any ideas how I can accomplish this?
Edit: So it seems all the research I've done that what I want to do is not possible without having the user install two separate apps, which is not ideal. There is a possibility that Google just made there Calendar app in that way (since they do write the source code), because I tested the top Calendar apps on the market and they all did not go off when the user force closed the app. So this begs the question, simply put, can this even be done in a single application? It is looking slim that it can be done due to Google trying to curb developers from not allowing the user control over random running broadcasts or services, which is understandable. Hopefully this helps others quickly realize there really isn't a real way to actually do this. All you can do is warn the user, that in closing your app in that manner they will not get alarms; enough said.
I would not use a Broadcast Receiver with your alarm
Broadcast Receivers in general provide two functions
1) Listen to broadcasts from the system
2) Allow apps outside of your own to make requests of your app.
Alarm Manager sets up new alarms with:
set(int type, long triggerAtMillis, PendingIntent operation)
Schedule an alarm. (from Android Documentation)
The Pending Intent placed in there is the intent that will be fired when the Alarm is up. You can have this Intent begin a Service by placing the service in the intent.
Ex:
Intent i = new Intent(context, MyService.class)
Where MyService is a class that extends Service. (you can just as easily do an activity, but having activities popping up out of nowhere is terrible design.
A Service is like an Activity that has no UI. Basically you can have it perform some background functions and possibly post a notification to the user that something has occurred.

Android AlarmManager stops firing after long periods of time

What is the proper context in which AlarmManagers should be declared and initialized so that they persist indefinitely (or until a system reboot or until a Task Killer wipes it out, realistically) and avoid garbage collection- But also allow for changes to the alarm across the scope of the entire application.
The whole reason I am using an AlarmManager is to remedy the need to have a service running at all times. However to accomplish this feat, where is the AlarmManager created? I would imagine it cannot be created in a service that is then turned off/destroyed because the alarm object would go away with it, eventually, and the alarms would stop firing- would it not?
I liked the idea of the AlarmManager example from a different question post, where a MyAlarm class is created as an extension of the BroadcastReceiver for the alarm's onReceive and the AlarmManager is intialized in this class' constructor. But how does this implementation work if a MyAlarm instance is required across multiple contexts. Such as, from event handlers across multiple Activity objects. From the event handlers of multiple Widgets. From within a worker service. All of which may have to disable or enable the alarm. My guess is, create a local instance everywhere you need to deal with the alarm and because the pendingIntent is the same for all instances, you will in effect be working with a virtual "singleton".
This is only conceptual at this point because I have no idea how I would test "garbage collection" of an Alarm Manager which could occur 8, 12, ? hours later when my alarm just randomly stops firing. My goal is, if the phone isn't rebooted, and my Task isn't manually killed by the user, to keep the alarm firing indefinitely. Thanks
In android the OS automatically closes services it thinks aren't being used... lots of apps and particularly widgets struggle with this. I don't know if there is a definitive way to make sure the service doesn't close itself. You can persist it longer by having it occasionally do something so the system doesn't think it's a useless task.
And yes, you'd use the same pending intent for all those, so you can just run one service with multiple handlers linked to it, that's part of the benefit of the way the OS is set up. You can update all widgets with your signature at the start of your single service, that service can also receive all the handlers from your widgets.
However to accomplish this feat, where is the AlarmManager created?
The AlarmManager is a system service. It is "created" by the operating system.
I would imagine it cannot be created in a service that is then turned off/destroyed because the alarm object would go away with it, eventually, and the alarms would stop firing- would it not?
The AlarmManager is a system service. The "alarm" are held by the operating system.
My guess is, create a local instance everywhere you need to deal with the alarm and because the pendingIntent is the same for all instances, you will in effect be working with a virtual "singleton".
Correct.

Best moment/location for setting an AlarmManager

My application shows content for a site that also has a notification system. I want to show if there are new notifications, and I am using an AlarmManager that calls an IntentService.
My question is: where should I start/register this AlarmManager? I've put it in the onCreate() of my activity just for proof-of-concept (and its working fine, thank you very much :) ), but if you would start that activity twice, you would get multiple alarms.
The only possible solution I've come up with is this, but I don't know if this would be best practice
Start the manager in an onCreate() if the preference "alarm started" is false
Set some variable that it is started in preferences.
Now if the alarm stops for some reason, there's no way to restart it. So, a variation would be:
Always call cancel in the onCreate()
And then always set the Alarm.
This seems like a common pattern: Wanting to periodically get information with an alarm, and not setting that alarm more then once. How should I do this? When, where and how to register the alarm?
Also, continueing on #Zelimir 's comment: can you check if a certain alarm is allready set?
Ideally, the alarm would be set regardless of the activity being started or not of course, but that might be another thing.
For completeness, this is the code I'm currently using to start the alarm:
AlarmManager alMan = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(this, CommentService.class);
PendingIntent penInt = PendingIntent.getService(this, 0, i, 0);
alMan.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
AlarmManager.INTERVAL_FIFTEEN_MINUTES,
penInt);
For even more completeness, the app description / situation.
The app is basically showing blogs (journals if you will) from a certain page. It has activities for adding entry, viewing entries, adding comments, etc. On the 'mother' site there is an option to recieve notifications (like the number you see here on SO too when you get a message). I want to show if there are new messages, and so retrieve them every xx minutes. It would be shown in the notificationbar for now, but it might feed some sort of widget later.
If you need more info: the app is called Androblip and it supports a site called blipfoto.com
When, where and how to register the alarm?
That is impossible to answer in the abstract. It depends entirely upon what the business rules are for your app, which you declined to supply in your question.
If the monitoring is to be happening all the time, a typical pattern is to register the alarm:
in onCreate() of your main activity for the very first run of your app
in a BOOT_COMPLETED BroadcastReceiver, to handle reboots, which wipe the AlarmManager roster
can you check if a certain alarm is allready set?
No, but you can cancel it without issue. Just create an equivalent PendingIntent and call cancel() on the AlarmManager.

Clarification of AlarmManager behavior in Android

I see all the examples of AlarmManager being set by an Activity.
My question is this:
If my application sets a recurring AlarmManager, does that persist even after the application that started is is closed and removed from memory?
If not, how do I start an AlarmManager at a lower level that is started by Android at boot up and if it ever fails or dies or throws an exception is restarted without the user having to do anything?
Lastly, if the action I wish for the BroadcastReceiver to undertake has no visual components, do I still have to create a separate Activity for it? In my case I want there to be a background uploader that wakes up and looks into a folder and if it sees files in that folder, sends them off to the server. I don't need any feedback to the user.
So, my ideal would be to have a magical, OS based AlarmManager that calls an IntentService which just handles the uploading, but I'm unclear on how to get such an AlarmManager running in the first place.
TIA
Yes, AFAIK the alarms "survive" and keeps getting triggered, even after the activity that registered them ends. But they don't survive a phone reboot.
If I understands your problem correctly, I think you can achieve what your looking for by creating a project with a broadcast receiver that listens for android.intent.action.BOOT_COMPLETED intents and then (re-)register a repeating alarm, which in turns starts a (Intent)Service to do the uploading.
You don't need an activity, but you probably would want one anyway, to let the user temporarily disable the upload mechanism by checking off a checkbox, or something. It would probably also be nice to let the user choose the frequency of your alarm, i.e. how often the service should be started and look for new files to upload. This would also be a good place to register your alarm for the first time.
I agree with Nicolai that you'd have 2 broadcast receivers in your application :
one that re-register the alarm on boot
one that starts your service when triggered by the alarm
You could still have an activity but it shouldn't be started by the alarm receiver (hence the service) : instead, maybe launch a notification as you start your service, with the user having the possibility to launch the activity from the expanded message of the notification.
maybe also think about setInexactRepeating (instead of setRepeating) for your alarm, as well as using a worker thread to handle the long uploads (in case the user wants to use your activity in the main thread at the same time).

Categories

Resources