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.
Related
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
I'm building an app and I need to schedule a notification that remember the user to access the app. I need this notification to be shown a month ahead of the last time the app was used
AlarmManager has access to the system alarm services. With the help of AlarmManager you can schedule execution of code in future. AlarmManager object can’t instantiate directly however it can be retrieved by calling Context.getSystemService(Context.ALARM_SERVICE). AlarmManager is always registered with Intent. When an alarm goes off, the Intent which has been registered with AlarmManager, is broadcasted by the system automatically. This intent starts the target application if it is not running. It is recommended to use AlarmManager when you want your application code to be run at a specific time, even if your application is not currently running.
There is an Example.
I think I have a design issue, but I will ask you guys the question.
My applications receives a broadcast in xbroadcastreceiver, i check some conditions and if true, I start a service, say zservice, using Alarm Manager with PendingIntent. Now in zservice, everytime when it is triggered by the alarmmanager, I check some other conditions and based upon a specific condition I need to cancel the alarm, I know alarmManager.cancel(pendingIntent);
will cancel the alarm, but my issue is how to get a access to the pendingIntent as it was in the xbroadcastreciever.
I have tried send pendingIntent to service but so far I am not successful so that when I need, I can turn the alarm off.
Hope this logic makes sense.
I know alarmManager.cancel(pendingIntent); will cancel the alram, but my issue is how to get a access to the pendingIntent as it was in the xbroadcastreciever.
Create an Intent with the same routing information (component, action, categories, MIME type) as the original. Create the same type of PendingIntent from this Intent as before (e.g., getService()). Use that PendingIntent with the cancel() call.
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.