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.
Related
I'm currently working on an app that handles alarms. I have reach the point where I can set the alarms using the AlarmManager and everything seems to work fine, but.. In all the examples that I found, and even in the Android official docs, I have seen people using a BroadcastReceiver for the PendingIntent, and then, calling an activity or whatever they need when the alarm fires. However, I have try to just pass a simple activity to the PendingIntent for the AlarmManager like this:
Intent intent = new Intent (getApplicationContext(), AlarmActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity (this, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.set (AlarmManager.RTC_WAKEUP, timeToTrigger.getTimeInMillis(), pendingIntent);
And after testing alarms in several contexts (app in foreground, app not running, etc..) I found that the activity is always been called correctly.
So, my doubt is as simple as: Why do people use a BroadcastReceiver and in the onReceive method call an activity if you can just simple call the activity directly?
Android frowns on this behaviour. You shouldn't interrupt the user if he is doing something else. This is why you don't want to launch an Activity from AlarmManager. Usually, you launch a BroadcastReceiver, which, if it wants to get the user's attention, will post a Notification. The user can then open the app via the Notification whenever he wants to.
Also, often you just want to perform some background process (like fetching data from a server, or updating some statistics), which doesn't require any user-interaction. In this case you would also launch a BroadcastReceiver or Service and not an Activity.
Starting with Android 10 there are more restrictions about background apps launching activities. Therefore, with current versions of Android it has become more difficult to do this. See https://developer.android.com/guide/components/activities/background-starts
So basically, even though it does work, it is not considered "respecting the user" and it probably won't work in the future.
First of all, I apologize for my English.
I need to sent data to a server and query for data in that server from time to time, maybe from week to week.
So I'm going to use the AlarmManager to do that. I wanna know if I can register two separated pending intents in the alarm manager, one to push data to the server, and another one to query data from the server. Can I register those two intents every time the application executes? Or do I need to test if I already got those intents registered?
I'm asking that because I'm afraid that I register one alarm to execute one week from now and then the user close the app and start again and the alarm is registered again one week from now and in the end will never be launched.
Another thing, is it safe to register a service to be executed with the AlarmManager?
Oh and this is the code I'm using:
long WEEK_IN_MILLIS = AlarmManager.INTERVAL_DAY * 7;
PendingIntent querySyncService = PendingIntent.getService(context, 0, new Intent(context,
QuerySyncService.class), PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent pushSyncService = PendingIntent.getService(context, 1, new Intent(context,
PushSyncService.class), PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.RTC, WEEK_IN_MILLIS, WEEK_IN_MILLIS, querySyncService);
alarmManager.setInexactRepeating(AlarmManager.RTC, WEEK_IN_MILLIS, WEEK_IN_MILLIS, pushSyncService);
I was thinking in putting this code inside some method and then just call the method every time the app start.
And I also saw that the solution I should probably use is the SyncAdapter, but the SyncAdapter just seems to be dirt, with a lot of code that does nothing, for example, you need one Stub Authenticator, then one Stub Authenticator Service and one Stub Content Provider and again one Stub Content Provider Service, then a lot of code for the Sync Adapter, then another bunch of code to only run the SyncAdapter. This just seems too dirty for me since I don't need a content provider neither one Authenticator.
You can, most certainly, register two separate Intents, with the AlarmManager. Just be sure to use different ids in your call to PendingIntent.getService... (and, btw, I suggest that you not use 0 as an id. 0s are just to easy to come by, in Java). Use the ACTION, or a parameter in the Extras, to distinguish between them.
There is no reason to use a BroadcastReceiver: you can fire the intent at the Service directly. If you are concerned about security, don't export the Service. A PendingIntent is fired from within your application, so there is no need to make the Service externally visible.
Finally, although you are right about the overhead for a SyncAdapter, it really is a great way to do exactly what you are trying to do. There is a good description of how to write one in EnterpriseAndroid (I am one of the authors). Once you get through the boiler-plate, it is actually almost elegant.
There is an example of a minimal SyncAdapter here.
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
Alright so I'm working on a project where I want the app to check the internet for updates. I also want the user to be able to customize how often it makes these checks.
I'm kinda struggling with how I go about doing this.
I'm thinking I have a BroadcastReceiver check for the Intent.ACTION_BOOT_COMPLETED, and then start the AlarmService based upon a variable already set somewhere.
But what happens if the user wants to update the interval? How do I stop the old AlarmService and replace it with the new one?
Also how do I have my AlarmService run a "background update" portion in my app without actually running any of the activities?
EDIT: Also can someone advise if I'm using the AlarmService correctly? I'd want to check for updates fairly frequently, should I be using some other method? I'd check between 1-5 minutes.
I do a similar thing in my app. I basically have two services: one that sets the alarm and the other that does the actual donwloading.
When your BroadcastReceiver get's the intent ACTION_BOOT_COMPLETED you start your alarmService which sends an broadcast to start the downloadService.
So your BroadcastReceiver does both things: starts the alarmServcie if intent == BOOT_COMPLETED else it starts the downloadservice.
Also if the user changes the periods in which he wants those updates to be done you store the chosen value in the preferences, start the alarmservice and retrieve the values to set up a new period, e. g. 1 hour, 2 hours
Edit: Here is the info some info from the docs about pending Intents and how to cancel them. http://developer.android.com/reference/android/app/PendingIntent.html
When the user changes the interval you should first cancel your old alarmservice and then set it again.
As stated in the Android Dev Guide, if you want a widget to update more often you should use the AlarmManager to set alarms that do not wake the device.
In principle: do not use the standard mechanisms provided by the AppWidgetProvider class and its easy set up with android:updatePeriodMillis in the xml file.
I am sorry to ask, but the guide just states "use AlarmManager, use RTC or ELAPSED_REALTIME, ..." but HOW EXACTLY do I send an intent to just update my widget is missing!!!
Could anybody please state the code I need to form the PendingIntent that mimics the default behaviour? I don't have a clue how to get the widget ids, which action I should use and so on... Sad the dev guide stops explaining at that point!
What is needed as extras to which action?
In case somebody is interested why I want to update more frequently than 30 minutes: my widget shows when the next bus departs at a station. There is a bus every 20 minutes, so I got two options: update the widget every minute, displaying the departure time of the next bus (that's what I want!!) or, not that good, state the departure time of the next bus so I will have to update at least every 20 minutes!
Now: when the device is asleep, of course, this should never wake it up - so my understanding of this section in the dev guide is that this is the correct way to implement it; anybody thinks I'm wrong?
Oh, another thing I would like to find out: if the device is asleep when the widget should be updated and my alarm is not issued due to the sleeping device, will it be updated IMMEDIATELY when it wakes up???
Thanks for your help!!
PS: I really wonder why the xml definition of a widget provider does not allow to simply state "don't wake the device" by a boolean switch... that would make life so much easier in the first place! What kind of a widget needs to wake device anyhow??? ;-)
This is how far I came up to now, but it does not work - nothing happens:
private void startAlarm(Context pContext) {
Log.d(TAG, "startAlarm");
AlarmManager am = (AlarmManager) pContext.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent("android.appwidget.action.APPWIDGET_UPDATE");
intent.setClass(pContext, getClass());
PendingIntent pi = PendingIntent.getBroadcast(pContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
am.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, pi);
}
Could anybody please state the code I need to form the PendingIntent that mimics the default behaviour?
I wouldn't, because AFAIK it is not possible to "mimic the default behavior" precisely. Simply have the AlarmManager alarm use a PendingIntent that either sends a custom action to your AppWidgetProvider that you process in onReceive(), or calls startService() on the IntentService you are using for the real smarts behind your app widget.
I don't have a clue how to get the widget ids
You saved the widget IDs from actual onUpdate() calls, such as when the app widget is first put on the screen. When your alarm goes off, update all your app widgets.
update the widget every minute, displaying the departure time of the next bus (that's what I want!!)
Please allow this to be user-configurable.
Oh, another thing I would like to find out: if the device is asleep when the widget should be updated and my alarm is not issued due to the sleeping device, will it be updated IMMEDIATELY when it wakes up???
AFAIK, yes, but I have not used alarms that are not of the _WAKEUP variety.
Don't know if they can still help you but I wrote some posts about Android Widget some days ago.
Here is the one regarding the AlarmManager:
http://malubu.wordpress.com/2012/06/05/take-your-time-widgets-and-alarmmanager/