How can I execute an action (maybe an Intent) on every specified time (e.g. Every day on 5AM)? It has to stay after device reboots, similar to how cron works.
I am not sure if I can use AlarmManager for this, or can I?
If you want it to stay after the device reboots, you have to schedule the alarm after the device reboots.
You will need to have the RECEIVE_BOOT_COMPLETED permission in your AndroidManifest.xml
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
A BroadcastReceiver is needed as well to capture the intent ACTION_BOOT_COMPLETED
<receiver android:name=".BootCompletedReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Lastly, override the onReceive method in your BroadcastReceiver.
public class BootcompletedReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//set alarm
}
}
Edit: Look at the setRepeating method of AlarmManager to schedule the 'Android cron'.
Using the BuzzBox SDK you can schedule a cron job in your App doing:
SchedulerManager.getInstance()
.saveTask(context, "0 8-19 * * 1,2,3,4,5", YourTask.class);
Where "0 8-19 * * 1,2,3,4,5" is a cron string that will run your Task once an hour, from 8am to 7pm, mon to fri.
You Task can be whatever you want, you just need to implement a doWork method. The library will take care of rescheduling on reboot, of acquiring the wake lock and on retrying on errors.
More info about the BuzzBox SDK here...
Related
I tried to make reminder application for Android, followed tutorial from this website Set Notification for Specific Date. Basically it used Alarm Manager to create a reminder.
This code used to call alarm manager and show notification on specific date.
reminderClient.setAlarmForNotification(calendar, uniqueid, interval);
I save all of the reminder in SQLite Database. So when this code (above) called, new record will be inserted to database and when notification show up, that record will be deleted.
The problem is whenever device restart, alarm manager stopped.
So I create a new BroadcastReceiver that receive event when device turned on.
<application ... >
<receiver android:name=".ReminderReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
</intent-filter>
</receiver>
</application>
public class ReminderReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//do stuff
}
}
Is it OK to get all record from database, and call setAlarmForNotification again inside OnReceive like this?
public class ReminderReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
List<MyReminder> reminders = database.getAllReminder();
Calendar cal = Calendar.getInstance();
for (int i=0; i<reminders.size(); i++) {
cal.setTime(parseStringDateToDate(reminders.get(i).getDateTime());
reminderClient.setAlarmForNotification(
cal,
reminders.get(i).getUniqueID(),
reminders.get(i).getInterval()
);
}
}
}
Or is there a better way?
BroadcastReceiver's onReceive() is called on UI thread so in common case it's not right to access a database or do any other file I/O in this method. For this task you need two things: background thread to give the system an ability to make its stuff in parallel and a Service to tell the system that it should not kill your process when onReceive() is finished. There is a component that gives you both things - IntentService. It's a Service and a working thread that finishes and stops a Service when return from onHandleIntent().
Also posibly you will need a WakeLock to ensure that all your calculations are finished and alarms are properly set. Look at WakefulBroadcastReceiver that is written just for this case.
NOTE: Actually in your case file I/O is really minimal and system boot probably is not a moment when every hundred milliseconds are crucial. But there is really no reason not to do things right.
User can create different alarms. So it's up to user when to keep alarm and he can keep multiple alarms and I maintain all the scheduled alarms in a database and show to the user for further reference. Below is my code.
if("CREATEONCE".equals(strparam1))
{
am.set(AlarmManager.RTC_WAKEUP, l2, pi);// l2 is time in millis
}else if("CREATEREPEAT".equals(strparam1))
{
am.setRepeating(AlarmManager.RTC_WAKEUP, l2, 86400000 , pi); //l2 is time in millis
}
So this is the code which sets the alarms. User can set multiple alarms. For example he keeps an alarm for 7.00 am for once, 8.00 am for once and 9.00 am dialy. So, for once alarms the code goes to if block and for repeat daily, the code goes to else if block in the code.
If the above 3 alarms are set by user at 6.00 am. If he reboots his device immediately after setting the alarms, the entire alarms don't trigger.
So I have read many posts regarding this like post1, post2. They all just gave to use broadcast receiver to know that device is rebooted. After the broadcast receiver receives a hint that device is rebooted, do I need to repeat above code again by getting the info from sqlite database to make all the alarms work? If so, can someone help me the way to do that from the broadcast receiver? Code snippets are appreciated
Suppose if the user sets 50 alarms, wouldn't it be a long process to get the info of all the 50 alarms and set them again?
I don't know how you are storing your alarms. But I suggest it would suffice to set up a system level alarm for the earliest coming alarm. Then once that is triggered, set up the alarm again for the next soonest triggering alarm.
I suggest putting your AlarmSetting call in a service and then call it from a broadcast receiver.
public class AlarmResetReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
//your code to set up alarms
}
}
There are also other conditions upon which you want to set your alarms up again in your manifest
<receiver android:name=".receivers.AlarmResetReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.TIME_SET" />
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
<action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter>
</receiver>
I've implemented AlarmManager to wake the phone once a day to perform an update task, update widgets and send notifications if applicable.
I'm using setRepeating and ELAPSED_REALTIME_WAKEUP
The alarm is triggered the first time (SystemClock.elapsedRealtime()+60000)
but it doesn't get triggered 86400000 milliseconds (24 hours) later.
Really struggling with this, I'm happy to accept im doing something wrong or if there are better ways to achieve what I'm trying to do. However I think my code looks like the standard thing people seem to do.
It's almost like the repeating alarm doesn't do what it says it should in all cases. If I reduce the interval to say 10 minutes it does work, my alarm triggers and the service is run over and over.
The nature of my app means updating more than once a day is overkill.
I need to find a realistic reliable solution to this.
Thanks for your time & hope you can point me in the right direction.
Here's my alarm implementation code...
Manifest:
<receiver android:name=".SystemChangeReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE" />
</intent-filter>
</receiver>
<receiver android:name=".UpdateAlarmReceiver" />
<service android:name=".UpdateService" />
<receiver android:name=".WidgetProviderSmall" android:label="#string/widget_small_label">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/appwidget_small" />
</receiver>
<receiver android:name=".WidgetProviderLarge" android:label="#string/widget_large_label">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/appwidget_large" />
</receiver>
SystemChangeReceiver listens for the boot broadcast, checks if an alarm needs to be set, if it does, it sets it.
SystemChangeReceiver:
#Override
public void onReceive(Context context, Intent intent) {
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.prefs_name), 0);
Boolean notifications = prefs.getBoolean("enable_updates", false);
if(notifications == true) {
Utils.setNotificationAlarm(context);
}
}
The setNotificationAlarm method, sets up the repeating alarm...
public static void setNotificationAlarm(Context context) {
AlarmManager alarmManager=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, UpdateAlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
alarmManager.setRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime()+60000,
86400000,
pi);
}
When the alarm triggers my receiver UpdateAlarmReceiver decides what to do and using WakefulIntentService runs my background update process, the handler for the service then updates widgets and sends notifications as required
UpdateAlarmReceiver:
public void onReceive(Context context, Intent intent) {
WakefulIntentService.sendWakefulWork(context, UpdateService.class);
}
There's nothing obviously wrong.
For a long period like that, though, I'd recommend RTC_WAKEUP, so you can arrange for it to occur in the middle of the night or perhaps at a user-selectable time, rather than every 24 hours from a semi-random start point. It's possible RTC_WAKEUP will give you better results.
Also:
Add a logging statement to onReceive() of UpdateAlarmReceiver to confirm whether you are getting control at all, or whether the problem is (gasp!) with WakefulIntentService.
Try steadily increasing your period. Since you say 10 minutes works, try an hour, then four hours, etc., and try to get a sense when it breaks down.
"I'm actually using AutoKiller Memory Optimizer it doesn't have a whitelist this could be the issue? My app is running still though according the process list." -- I would disable all task killers, just to be sure they are not interfering.
Have you tried setting it not to repeat. Then when it fires off you set the next single shot alarm for 24 hours later? This would function like a repeating alarm but might avoid some of the problems you are seeing.
Code that schedules alarm.
PendingIntent sender = PendingIntent.getBroadcast(this, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, time, sender);
Its working fine, but when I kill my app in task killer, I lost my scheduleed alarm. How to solve this problem?
have your application broadcast a message as its being killed, and when this message is broadcast, then have a listener check if the service is still running.. if its not run it. This will insure that your service is running even if the application is killed.
Update
I'll try to create a flow diagram for you
The onDestroy() method is part of a service.
I hope this helps.
UPDATE 2
One thing I forgot to mention is the fact that you ideally only want one instance of the service to be run. So just looking at the ID that is present within the onStart() should be == to 1 to start it else.. ignore it.
Methods of notice of the Service Class:
onStart() : This method is called when the service is being started
onDestroy() : This is the method that is called when a service is being killed
Methods of notice of the BroadcastReciever class:
onReceive(): This methods receives all intents that are sent to it (unless filtered)
Look up examples on BroadcastRecievers (Message Broadcasting) and Service (Starting a service)
References:
http://developer.android.com/reference/android/content/BroadcastReceiver.html
http://developer.android.com/reference/android/app/Service.html
Alarm set by alarm manager is not killed when app is closed, how ever when a reboot occurs all alarms are cleared by the os since there is no persistence. So you need to do the persistence.
Every Time while setting a alarm save the alarm time.
Register a receiver for boot completion.
Set the alarm again on reboot.
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//re register the alarm
}
}
Manifest.xml
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
.......
<receiver
android:name="BootReceiver"
android:enabled="true"
android:exported="true"
>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
You could use SharedPreference to save the time (time at when the alarm should be triggered or time at when it should be triggered next)
Use that to set a new alarm at the boot receiver.
As my code looks today, I'm periodically sending a alarm(?) using AlarmManager that is received by AlarmReceiver extends BroadcastReceiver which in turn starts a Service. The Service do some updating and ends with a stopSelf(). IMO this is the best way of periodically perfom a task without constantly having a Service running. Correct?
The issue with this code is however that the whole chain of events is initiated onSharedPreferenceChanged(). I (initially) thought this was a good idea since the whole updating thing is enabled by the user in SharedPreferences.
I've now come to the conclusion that this is in fact not very good and that I need to initiate the AlarmManager/AlarmReceiver/Service/whatever both onPreferenceChange but also on boot.
I've done some searching but everyone seems to want to start the Service on boot. As I see it, I just need to initiate the AlarmManager which will then start the Service (when needed and only periodically).
Please help me with, first of all, sorting this out and secondly coding it!
Thanks in advance!
Then, create and register a BroadcastReceiver where you will do the AlarmManager stuff:
public class YourBootReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
// do the AlarmManager here
}
}
Then, on your manifest:
<application>
... other stuff
<receiver android:name=".YourBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />