Timers and Background Work

Ivana Tanova
6 min readOct 30, 2020

It is well known, thanks to Alice and Einstein, that time is not exact- it is relative. Relative to our own reality. Let’s examine how our programming world’s reality uses time.

What you will learn:

  • Wall clock and CPU clock — what is the difference.
  • Why you should not sleep in the background. (Trust me, I will explain)
  • How to implement reliable timer in Android.
  • Fun facts about time and the universe

Wall clock and CPU clock

In the deep past, people used to rely on shadow-clocks. It shows something different at different ends of the Earth, right? Our wall clocks do the same, there are different time zones and when you travel it gets complicated. The same is true for clocks used in programming, they use the same time zones and one rule to remember is — it is important who is telling you the time.

Wall Clock

In programming “wall” clocks measure time in milliseconds passed since January 1st, 1970. This specific year is called Unix epoch, and it is used in all Unix systems. Windows systems use another one. It is just a random point in time, a system to count. We can say that our wall clocks do the same — they count seconds passed since 00 hour. The main difference is that when the seconds hit 86400, time is restarted.

Back to Java/Android programming, this is exactly what System.currentTimeMillis() returns. But think critically — how it could measure the quantity of milliseconds that passed? It should know what time is now (date and time) and the only input for this data are the user or the phone network. Are those sources reliable?

❗️Important! Don’t rely on that time if you are waiting for your app’s free trial to end or for your session timer. The user can easily hack you by just entering some time in the future. Then you have to wait few more years for your bucks.

Why do we have it then?

✅ Use this clock for:

Calendar or alarm clock applications. Applications that depend on the user’s reality for the time. The user will cheat himself if he gave us the wrong time.

❌ Don’t use it for:

Interval or elapsed time measurements. Applications that rely on accurately passed time — timers. They rely on our science-based perspective for the reality in which the world is rotated for 86400 seconds and nothing more or less.

CPU clock

Now we are in a well-known field — CPU is hardware, we know how it works, we can switch it on and switch it off, it is power dependent. Cmmon everyone has needs! CPU clock refers to milliseconds since the system was booted. In our closed system, when CPU is on, this is our sunrise and our sunset is when the device is off. Great, so we have a reliable time that is not affected by our users. Almost! There are two implementations and we need to choose carefully.

This method measures CPU time. The problem with it is that it ticks as long as the CPU does. In Android systems, CPU enters so-called deep sleep when battery save mode is on, CPU is off, the display is dark, or the device is waiting for external input. On modern Android devices with Doze and App Standby, it enters deep sleep when we turn battery saver on, and the device is detached from a cable.

❗️Important! Don’t rely on that timer for time measure that needs to survive in a background state.

✅ Use this clock for:

  • Interval timing that should be active only when the screen is on. For example, you have a button that prevents multiple clicks disabling itself for a few milliseconds after user’s click event. The moment your screen is off, you don’t care.

❌ Don’t use it for:

  • Background working timers, alarms, session (unless you restart your session the momet the screen is active), bucks receiving, dates.
  • elapsedRealtime()

Return the time since the system was booted, and include deep sleep. This clock ticks even when CPU doesn’t. This is the recommended one for general purposes time counting.🥳 🥳 🥳

✅ Use this clock for:

Measuring elapsed time, even when your app is in the background. Sessions.

❌ Don’t use it for:

Alarms and date time counting.

Don’t sleep in a the background!

We can very naively think that we don’t need to care about wall and CPU time. We have Thread.sleep and coroutine’s delay methods. Sure… A core fundamental in programming is to use something only when knowing how it works. So let’s see how these methods work.

  • Thread.sleep()— Under the hood, this method uses uptimeMillis() . So the moment your device goes to sleep, the remainder of the time will be postponed until the device wakes up. You give this method 5 seconds and you want to show a sanackbar exactly after 5 seconds, but those seconds won’t be science, “real world” based, those will be “screen has been on” for 5 seconds. If 3 seconds have passed and then CPU starts sleeping for 2 seconds, the moment CPU is awake you will expect your timer to finish, but what will actually happen is — CPU will count another 2 seconds because it doesn’t know how many seconds it has slept. It is not stupid, it just doesn’t know!
  • Coroutine.delay() -The implementation here is a mystery to me. All that the documentation state is:

how exactly time is tracked is an implementation detail of CoroutineDispatcher in the context.

I found a Medium article claiming that delay() is implemented via Handler.postDelayed() method. Sounds reasonable, because Handlers are the core API for Android asynchronous programming.

  • Handlers — Handler objects also use the uptimeMillis() clock .

Here is the source code for Handler’s method sendMessageDelayed:

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

❗️ So, don’t use delay() for your “real” world, french toast timer. If the CPU enters sleep mode, your kitchen may be on fire. 🔥

A little drama helps with learning 😉, so now spend some time repeating:

delay = fire 🔥 🔥

How to implement reliable timers in Android

AlarmManager

You should consider AlarmManager if you really, really want that work to be executed at the exact time, no matter what. It triggers events even when the device is in deep sleep or even when your application is not running at all. It has two options: To use wall clock or to use CPU- elapsedRealtime() watch. ❗️Beware — starting with API 19 you need to use method setExact() otherwise, our french toast is gone. 🔥

So if you are preparing breakfast, then AlarmManager + ELAPSED_REALTIME + setExact() is not a bad option.

Obviously, AlarmManager will not trigger the alarm if the device is rebooted, because it uses CPU. Recall our sunset is when CPU is off, elapsedRealtime() could not survive once the CPU is off. The clock is restarted, it is a new day, new you.

Handler

What Android guys state in the documentation is:

For normal timing operations (ticks, timeouts, etc) it is easier and much more efficient to use Handler.

So back to our sleep and delay methods, they most probably use Handler. Use them, but restart the time, every time you screen is on, just for any case, you never know if CPU has been in deep sleep.

Elapsed time

elapsedRealtime()returns the time since the system was booted, and include deep sleep.

Fun Facts

  • The study and science of time measurement is called horology.
  • Prior to 1961 there was no UTC, and prior to 1958 there was no widespread atomic timekeeping; in these eras, some approximation of GMT (based directly on the Earth’s rotation) was used instead of an atomic timescale.
  • Unix enthusiasts have a history of holding “time_t parties” (pronounced “time tea parties”) to celebrate significant values of the Unix time number. These are directly analogous to the new year celebrations that occur at the change of year in many calendars.At 01:46:40 UTC on Sunday, 9 September 2001, the Unix billennium (Unix time number 1000000000) was celebrated.
  • Egyptians obelisks probably has been used as a shadow-clock. The moving shadows formed a type of sundial, enabling citizens to divide the day into two parts.
  • Cesium clocks are so accurate that they will be off by only one second after running for 300 million years.

References

https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html

https://developer.android.com/reference/android/os/SystemClock#uptimeMillis()

https://medium.com/@f2016826/timers-vs-handlers-aeae5d3cb5a

--

--

Ivana Tanova

Android enthusiast and keen tea drinker. I love to learn and to be funny, both for me are passions.