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.
Related
I have relatively simple setup that should trigger an alarm at certain time of the day and show a notification to user. here is relative code,
Setting the alarm
long inTime = /*expirationTime*/ Calendar.getInstance().getTimeInMillis() + 10000;
Intent startIntent = new Intent("parking.event");
startIntent.setClass(getBaseContext(), ParkingExpirationWarmingBroadcast.class);
PendingIntent startPendingIntent = PendingIntent.getBroadcast(this, 99, startIntent, 0);
alarmMgr.setExact(AlarmManager.RTC_WAKEUP,
inTime,
startPendingIntent);
BroadcastReceiver registered
<receiver
android:name=".modules.parking.ParkingExpirationWarmingBroadcast"
android:enabled="true">
<intent-filter>
<action android:name="parking.event" />
</intent-filter>
</receiver>
Broadcast Receiver
class ParkingExpirationWarmingBroadcast : BroadcastReceiver() {
#SuppressLint("NewApi")
override fun onReceive(context: Context, intent: Intent) {
}
}
The receiver is only getting triggered if app is in background. as soon as i swipe the app from multitasking, the notification is cleared and no new Alarms are triggered. I checked this setup on Android 7.0 and BroadcastReceiver is triggered regardless of app running or not.
I am aware regarding restrictions over implicit broadcasts in Android Oreo but i don't believe the intent that i have mentioned above is considered implicit.
Can anyone point out what i am doing wrong?
This is a general behavior of any Android's version. If you force-quit an application, then its Alarms and PendingIntents are deleted as well.
You can find the same answer here and here.
Force closing an app destroys its components . This is what force stop does. It's not a bug, it is very much a feature. Follow the following thread , it has been discussed by android framework engineers .
https://groups.google.com/forum/?fromgroups=#!topic/android-developers/anUoem0qrxU
I made this broadcast receiver
public class DateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("DATE RECEIVER", "OK");
}
}
registered in manifest
<receiver
android:name=".DateReceiver"
android:enabled="true"
android:exported="false" >
<intent-filter>
<action android:name="android.intent.action.DATE_CHANGED" />
</intent-filter>
</receiver>
It works when device is on but if I turn it off and wait for midnight to pass, then I don't get any intent at reboot. How should I get it?
You won't get this broadcast because date has NOT changed (unless i.e. there's time update after the boot). It is different but has not changed in the way that justifies this broadcast. This may be confusing but in fact it does not matter what time stamp is when device starts. As device was started it does not know if that was because of restart or it was off for 5 weeks. Broadcast will be send if time is artificially changed i.e. due to network time sync, manual time change via preferences, timezone change. Normal ticking does not count. Initial time stamp does not matter.
If you need to know date on boot, you should listen to BOOT_COMPLETED.
You also need to remove android:exported="false" or set it to true as otherwise it is not reachable.
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>
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" />
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...