Building robust timers from scratch is deceptively complex. Consider the challenges: you need thread-safe timer management that gracefully handles cancellation and rebuilding, properly accounts for system time changes, and ensures timers never execute concurrently. KOS handles all of this complexity for you with callbacks. Callbacks in KOS are timers, and only named this way to differentiate from Java’s default implementation of timers. There are a couple use cases for when you’ll use a callback. For example, rather than writing complex logic to manage a configurable delay timer that needs to be torn down and rebuilt when the delay changes, you can simply use AdjustableCallback. When a retry mechanism with exponential back off is needed you can use BackoffCallback. Need a dead-man’s switch that extends its timeout on each event? PushPullCallback handles this with a single method call. These callbacks provide guarantees that are easy to miss when building your own timers – like ensuring proper cleanup of resources, handling edge cases around time changes, and maintaining thread safety.
Sometimes the instantiation of a new object is not necessary and all that is desired is for some code to be executed in a certain amount of time from the present moment. For that use case one can use the scheduleCallback(Runnable command, long delayMs) static method from KosUtil.
Timer Concepts
Delay Variance
Delay variance is the process of applying some degree of randomness to the exact delay of the callback. In KOS this is done by specifying a min-variance and max-variance. These are decimal values which represent a percentage. A random variance is then selected from this range and applied to the delay. For example if your min-variance was 0.75 and your max-variance was 1.25, then the your delay would be somewhere between 75% and 125% of your delay. Delay variance is used to prevent timers from synchronizing over time and causing load spikes, by manually injecting an element of randomness into the system. All callbacks in KOS have no delay variance, by default but the min-variance and max-variance can be set via setMinVariance() and setMaxVariance() respectively, for those callbacks which support it.
AdjustableCallback
Use Case
This callback is designed for general timer functionalities, whether the timer is only needed once, or repeatedly. It is also easily configurable and can be changed as needed. This timer also supports delay variance and can be used in situations that require it.
Description
This class serves as the primary callback for your applications. It supports both single and recurring callbacks, allows for delay adjustments while the timer is active, and includes delay variance support to introduce a degree of randomness to the timing.
setDelay(int ms)
The setDelay() method enables you to modify the timer’s delay. If the timer is already running when you change the delay, the elapsed time will be accounted for. For example, if a timer is initially set for 60 minutes and has already counted down 45 minutes, changing the delay to 15 minutes will mean the timer has effectively waited long enough to trigger from a real-world perspective. Note that this method does not start the timer.
setAbsDelay(int ms)
This method sets the timer’s delay to the specified value. If the timer is stopped, it will remain in that state. However, if it is currently running, the timer will stop and restart with the new delay.
setDayRelative(int secFromMidnight)
This method sets the delay relative to the current local time, measured in seconds from midnight. This ensures that the timer remains accurate despite any changes to the system time. For example you might want to perform some function at 2am. This method allows the callback to be configured to occur once a day at the time that is relative to the context of the machine.
BackoffCallback
Use Case
This class is ideal for scheduling callbacks that utilize a back off algorithm. It’s commonly used in network communication where immediate retries may be counterproductive – for example, when a service is overloaded, increasing the delay between attempts allows the system to recover while reducing network congestion.
Description
The class utilizes the Backoff class to calculate back off intervals, and its behavior can be managed through the provided BackoffConfig object. When you start this callback, it resets the back off count to 0 and starts the timer. Using this class is straightforward: simply instantiate it and call the start() method. When you no longer require the back off functionality, just call cancel().
PushPullCallback
Use Case
This callback is used when the time that code is to be run is affected by external events. For instance take an interactive smart screen at a mall, which when idle shows advertisements, but when in use is showing the mall directory. If this device were running KOS it would use a PushPullCallback to trigger the transition to the idle content. For instance say you wanted the transition to occur 2 minutes after the last touch event. On each touch event you would call the push() method passing in 120,000ms and the callback would reset and wait this time before calling the transition code.
When the inverse behavior is desired this callback provides the pull() method to perform that function.
Description
The PushPullCallback is a callback which allows the user to both extend (push) and reduce (pull) the time when it runs the code provided. Vital to PushPullCallback are three methods:
start(long delay)
Using this method will cause the timer to act in the typical manner. This means that when called, if a timer is already running, start() will stop it before creating a new timer with the specified delay.
push(long delay, boolean start)
As mentioned before, push is responsible for extending the timer. Every time it is called, the timeout of the existing timer is reset based on the new delay passed to the method. It is important to note that the new timeout is only used if it is greater than the existing timeout. This is to say that the push() method will never make the timeout shorter.
pull(long delay, boolean start)
This method is responsible for making the timer shorter. Like push(), when called it will compute the new timeout using the new specified delay. If the new timeout is greater than the old timeout, the old timeout is preserved, meaning pull() will never increase the timeout.
Beyond that, if the start parameter of push() and pull() is set to true, the methods can act like start() and begin a timer.
Having established the basics behind how PushPullCallbacks work, you may be wondering what sets them apart from adjustable callbacks. Generally speaking the difference is that AdjustableCallback allows for more variability when it comes to the delay. One difference is that AdjustableCallback supports delay variance while PushPullCallbacks do not. The ability to set delays by date and set an absolute delay also differentiates AdjustableCallbacks from the others.
TriggeredCallback
Use Case
This class is primarily used to aggregate many events into a single delayed callback allowing a single timer to be managed as opposed to creating a new timer for every event that occurs.
Description
A TriggeredCallback is a callback designed to ignore any additional triggers called during the specified delay period. Triggers commenced by calling the trigger() method. The api for this callback is simple, additionally this callback supports delay variance.
