I am writing a reminder app for Android that repeatedly sends the user a notification with increasing intervals in between. Namely after 30s, 2m, 10m, ..., 25 days, 4 months, 2 years.
I originally intended to do this by registering a JobService that would run every 30s to check whether it was time to send a notification. However, as this post warns (and as I found out):
Which means that my job runs at most once every 15 minutes, and prevents me from sending my 30s, 2m and 10m reminders.
What would be the correct/most efficient way of implementing such functionality?
(Also, running a job every 30s to check for the 2y notification is quite inefficient)
Don't make it a periodic job. Make each next schedule a one-time event. Each time the job is triggered schedule a new item.
Related
I'm programming an update interface in my Android Things project. I can do manual update, with an user input. But I'm trying to schedule an auto-update every night at midnight. I want to use a custom UpdatePolicy with a deadline but I failed to use it.
I tried this in the onCreate method in my activity :
mUpdateManager.setPolicy(
new UpdatePolicy.Builder()
.setPolicy(POLICY_APPLY_AND_REBOOT)
.setUpdateDeadline(10, TimeUnit.SECONDS)
.build());
But there isn't any update after 10 seconds.
Maybe, I don't understand the deadline.
Do I use it wrong ?
The deadline has nothing to do with when an update check is performed. The usual schedule of update checks is
once shortly after boot
once every 5 hours (approximately) thereafter
(These times are not exact for reasons that aren't relevant to this discussion.)
The deadline reflects how long the device will let an available update sit without being applied before the device will force it to apply and reboot. The device doesn't know about an available update until it performs a check, so you could be waiting up to 5 hours for that.
The deadline is meant to operate on a longer timescale (for instance, 5 days, a week, etc). This is useful as a fallback in case there's some kind of bug with the update scheduler, or in case you allow users to postpone the update but don't want them to be able to do that forever.
To achieve what you want, you should schedule (using WorkManager, JobScheduler, etc) a task that runs at midnight each day and calls UpdateManager.performUpdateNow(UpdatePolicy.POLICY_APPLY_AND_REBOOT)
TL,DR: Update checks are very much a background thing. If you care about timing at all, use UpdateManager.performUpdateNow, but no more than once every 5 hours.
In my system, user can create a schedule with time and conditions. Before 30 mins of schedule time, if the conditions are not satisfied the system will raise an alarm to notice users about that.
My system are spring boot applications and using spring scheduled task to trigger alarms. The problems is when user creates a lot of schedule in the future, if I create a scheduled task for each schedule data, there will be memory problem.
My current solution is a create a schedule run at a time of everyday to scan all data in next 24 hours and create scheduled task for them to trigger alarm. This will reduce scheduled tasks created but if user creates new schedule data in next 24 hours after scanning, that data will be not trigger any alarm.
So what should I do?
Is there a reason that you are scheduling all of this in JVM memory? If the JVM crashes (or is simply rebooted), the timers would then be lost as if the user never scheduled any alarm. As you mentioned, creating a timer per request would likely not be a scalable solution.
Without knowing the specific details of your system, the most common approach would be to persist (i.e. in a DB, flat file, etc.) the data each time a user requests to schedule event. This way, in the event of a crash or reboot, you won't lose events. Similarly, this approach can scale to multiple servers if necessary. Then, at whatever granularity you support (i.e. minute, hour, day, etc.) there would be a process or thread (only a single monitor thread) find all of the events which have expired since you last ran. Finally, once this thread has identified events that need an "alarm," this one thread can control sending these events for active processing. This thread can either individually handle each event or otherwise submit them to an active work queue for parallelization.
More specifically, if you have alarms which could go off at any minute, you should schedule a monitor thread to run every minute. This thread should find all the events which require an alarm and then actually send that alarm.
Remember that how often you should schedule your monitor thread is a function of the resolution you want for your alarms and your tolerance for late alarms. If late alarms are totally unacceptable, then your monitor must run at least as often as the finest granularity for scheduling an alarm event. This is, of course, assuming alarms are always scheduled in the future-- otherwise, you will probably want to double the frequency of your monitoring checks. To see why, consider the following example:
minute 0: Run monitor
minute 0: User schedules alarm for minute 0
minute 1: Run monitor
If we run the monitor once per minute but allow the user to schedule an alarm in the current minute, it's quite possible that we'll miss the event (as shown in the example above). I can go into this more deeply if necessary, but this is here mostly for completeness as I have no indications from your description that this will actually pose any problems.
Good luck.
AlarmManager on API19 has the method setExact() to set an exact alarm.
Exact means --> If I set an alarm to 2:01 pm it will be triggered at 2:01 pm
On API 23 - Marhsmwallow (6.0) there is a new method setExactAndAllowWhileIdle(), but as of the reference it is not EXACT because it will trigger only every minute and in low power idle mode only every 15 minutes.
Exact != every 15 minutes :-)
So how can I achieve an exact alarm with AlarmManager in 6.0?
If a user adds a reminder or a calendar appointment and wants to be informed 10 minutes before the event it should show the alarm EXACT 10 minutes before the event. With setExactAndAllowWhileIdle() this seems is not possible.
Reference Link:
http://developer.android.com/reference/android/app/AlarmManager.html#setExactAndAllowWhileIdle(int, long, android.app.PendingIntent)
So how can I achieve an exact alarm with AlarmManager in 6.0?
You are welcome to try setAlarmClock(), as AFAIK it is unaffected by Doze mode. Otherwise, AlarmManager is not a viable option for you. Even having your app on the battery optimization whitelist will not help, as AlarmManager behavior does not change based on the whitelist.
You are welcome to use GCM, as a high-priority message should give you an opportunity to alert the user. This, of course, requires network connectivity.
The only offline solution that I am aware of — and that I am presently testing — is to have the user add your app to the battery optimization whitelist, then use a foreground service (to try to keep your process around), a ScheduledExecutorService (for the timing), and a partial WakeLock (to keep the CPU on). This will be fairly devastating to the user's battery.
Using setExactAndAllowWhileIdle() for a one-time alarm will fire exactly on the given time even in Doze idle mode. So this probably is the way to go.
Problems start, if you want to repeat the alarm at a rate of < 15 min (or set any other at a time < 15 min away from the last one), as this will not work in Doze idle mode, where such alarms are forced to the next 15 min or are executed when idle maintenance starts, which happens for about ten minutes first after 1 hour, then after another 2 hours, then after another 4 hours and so on.
- EDIT -
As of today Nov 17, Dianne Hackborn writes in this Post's comments:
"For what it's worth, the minimum time between while idle alarms will be changing to 9 minutes at some point relatively soon (even on devices running the current Marshmallow builds)."
This doesn't change anything fundamentally though.
Here are my discussion with Ian Lake on Google+!
setExactAndAllowWhileIdle() is exact and should work.
The 15 minutes time frame is wrong in the java doc.
I was trying to create an automation system running in the background. My frequency range was between 1-15 minutes. My wish was not to use a foreground service. By looking at the name of the method "setExactAndAllowWhileIdle", I thought that yeah it is safe to go with one-time alarms, scheduling the next one when done.
However, I couldn't find a way to run code in doze mode with alarms running more frequent than 15 minutes. Instead, I choose to start a foreground service when doze mode gets activated and stop that foreground service when phone awakes. User won't be seeing your foreground notification while using his/her phone. I don't care much about the ones in doze mode.
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if(intent.getAction().equals("android.os.action.DEVICE_IDLE_MODE_CHANGED")){
if (pm.isDeviceIdleMode()) {
//startAutomationForegroundService();
} else {
//stopAutomationForegroundService();
return;
}
AutomationReceiver.completeWakefulIntent(intent);
return;
}
}
You need to register "android.os.action.DEVICE_IDLE_MODE_CHANGED" intent filter into your WakefulBroadcastReceiver. Care putting it into manifest may not help.
I need to implement the following functionality:
I have something like a messenger. So, user sends messages, and system should wait 3 sec after last message. So, for example user sends 'Hello!',and system should start countdown, and if 2 seconds passed and user sends 'hey!', it should start counting down again. If 3 secs has passed, special action should fire. Are there any special classes and technologies for this in java and java frameworks?
UPDATE:
Basically I need something ready to implement a time barrier with ability to change waiting time. So, I need to wait 3 secs after LAST message came. So, when API gets message 'Hello', it should start waiting for 3 secs before perform some action. If 2 secs passed and API gets next message 'Hey', it should init it's time counter and start counting down seconds again. So, totally 5 secs are passed. And If there's no message to API for 3 secs after last message, action should run.
If I do Thread.sleep or future.get I specify fixed number of secs to delay, but I want to have ability to change this number online and system should take it. For example:
int counter=5;//any shared seconds quantity holder
while(true){
counter--;
Thread.sleep(1000);
}
....
setCounter(5);//I call somewhere, and loop will be looping again.
But I am looking for something stable and tested, not to invent a bicycle.
I need to running thread every one second. But when application killed, the thread must be still alive.
My thread task is used for increment Unix Timestamp (that synchronized when the first time application running from our server time) by one every second. I need to create this task because in some device, date time can changed unpredictable (maybe low on battery, hard reset, dropped or something else).
My Activity must be get that Unix Timestamp value when it needed.
From SO, Alarm Manager is not a good choice,
I would recommend you not to use an AlarmManager for 30 seconds, as some have suggested. Because 30 seconds is too short. it will drain the battery. For AlarmManager use a minimum 1 minute with RTC.
Other people suggest using Timer Task or ScheduledExecutorService, what the best thread to fit my need?
Thanks.
You would never achieve that. Any process could be killed by System. And task running every seconds is horrible (like AlarmManager said).
One idea is: save your server time and device time such as SystemClock.elapsedRealtime() . (do not use System.currentTimeMillis() for this purpose. ... this is display time for user and can be changed by user or something).
When you need time later, get elapsedRealtime() again and compare with stored elapsedRealtime(), and add this diff to stored server time. You will get desired time.
Or simply ask current time to your server , depends on needs :).
If you want to care hard reset I think that you should have database on your server to manage the first time when user launches app.