Developing your own time display application is a classic task that almost every developer faces when learning Android SDK. This is not just a way to find out the current time, but also an excellent testing ground for practicing skills in working with Handler, Runnable, as well as updating the user interface in real time. Understanding how to correctly implement periodic tasks is critical to creating any interactive applications.

In this article we will look at two main approaches: creating a simple digital signage application and developing a full-fledged desktop widget that will work even without the main application running. You'll learn how to format dates, work with SimpleDateFormat and properly manage the life cycle of components so as not to drain your device's battery.

Before you start coding, you need to make sure that your project is configured correctly. We will use the language Kotlin or Java (the syntax will be clear in both cases), as well as the standard AndroidX libraries.

โš ๏ธ Warning: Uncontrolled use of timers or loops with short intervals can lead to excessive power consumption and heating of the device. Always stop updates when the application or widget is not visible to the user.

Basic project structure and XML markup

Let's start by creating an interface. To display the clock, we need TextView, which will occupy the central part of the screen. In the markup file activity_main.xml we will set the font and alignment parameters so that the time is read clearly and looks aesthetically pleasing. Using ConstraintLayout will allow you to easily position elements on different screens.

Pay attention to the attribute android:textSize. For a watch, it should be large enough so that the numbers are clearly visible. It is also recommended to use a monospace font or a font with a fixed width of numbers so that the text does not โ€œjerkโ€ when changing seconds, although in modern versions of Android this is solved automatically.

<TextView

android:id="@+id/clockText"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="00:00:00"

android:textSize="48sp"

android:textStyle="bold"

app:layout_constraintBottom_toBottomOf="parent"

app:layout_constraintEnd_toEndOf="parent"

app:layout_constraintStart_toStartOf="parent"

app:layout_constraintTop_toTopOf="parent" />

After creating the markup, you need to associate it with the application logic. In the class Activity we will find our TextView by ID. This will be the entry point for time data. Don't forget to add the necessary imports to work with dates and formatting.

๐Ÿ’ก

Use string resources (strings.xml) for static text, but this is not required for dynamic time, since the text is generated programmatically every second.

Time update logic in Activity

The heart of our clock is the update mechanism. We can't just start an infinite loop while(true)as this will freeze the interface and cause the application to crash (ANR). Instead, a class Handleris used that allows you to schedule tasks to run on the main thread (UI Thread).

The algorithm is simple: we create an object Runnablethat updates the text in TextView and then requests that it restart itself after 1000 milliseconds (1 second). This ensures the seconds tick by smoothly. Time formatting is done through a class SimpleDateFormat with a pattern "HH:mm:ss".

  • ๐Ÿ•’ Create a variable Handler to manage pending tasks.
  • ๐Ÿ“ Define Runnablethat will receive the current time and update text.
  • โฑ๏ธ Run the first run manually and inside Runnable call postDelayed.
  • ๐Ÿ›‘ Be sure to delete all requests to Handler in the method onDestroy().

Particular attention should be paid to the Activity lifecycle. If the user minimizes the app or flips the screen and we don't stop Handlerthe updates may continue in the background or cause a memory leak. The method removeCallbacks() should be called when the Activity enters the stopped state.

๐Ÿ“Š What time format do you prefer to see on the screen?
12-hour (AM/PM)
24-hour (military)
With seconds
Without seconds

Implementing an analog clock with Canvas

If you want to create not just numbers, but a beautiful analog clock with hands, you will need a class Canvas and a custom one View. In this case, we draw a circle on the clock face and then calculate the rotation angles for the hour, minute, and second hands based on the current time.

The math here is as follows: a full circle is 360 degrees. The hour hand moves 30 degrees in one hour (360/12), plus a fraction of a degree in minutes. Minute - 6 degrees per minute. The second is also 6 degrees per second. Using trigonometric functions sin and cos, we calculate the coordinates of the ends of the arrows.

Component Rotation angle (degrees) Calculation formula
Hour hand 30ยฐ per hour (hours % 12) 30 + minutes 0.5
Minute hand 6ยฐ per minute minutes 6 + seconds 0.1
Second hand 6ยฐ per second seconds * 6

Drawing occurs in the method onDraw(). It is important to call invalidate() after each time update so that the system redraws the view. However, doing this 60 times per second for a clock is redundant; updating once per second is enough, which saves GPU resources.

โš ๏ธ Attention: When drawing on Canvas, avoid creating new objects (for example, Paint or Path) inside the onDraw method. Create them once during initialization, otherwise you will trigger frequent launches of the garbage collector (Garbage Collector), which will cause interface freezes.

Creating a widget for the desktop

App Widgets allow you to display the time on the desktop without launching the main application. To do this, you need to create an XML configuration file in the folder xml and a descendant class AppWidgetProvider. This is a more complex, but also more useful option for the user.

In the manifest file AndroidManifest.xml you need to register the widget, specifying its meta data and intent filter. The Android system itself will manage the life cycle of the widget, calling the method onUpdate when adding a widget or after a specified time interval.

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"

android:minWidth="110dp"

android:minHeight="40dp"

android:updatePeriodMillis="1000"

android:previewImage="@drawable/preview"

android:initialLayout="@layout/widget_layout"

android:resizeMode="horizontal|vertical"

android:widgetCategory="home_screen" />

However, the attribute updatePeriodMillis has a limitation: the system will not allow updating the widget more often than once every 30 minutes in order to save the battery. This is not suitable for seconds watches. Therefore, for frequent updates, AlarmManageris used, which wakes up the application exactly at the right second.

โ˜‘๏ธ Checklist for the widget

Done: 0 / 4

Working with AlarmManager for exact time

To the widget was updated every second, we must use AlarmManager. This system service allows you to schedule a launch PendingIntent at a strictly defined time. The logic is as follows: the widget requests an update after 1 second, the system wakes up the application, the application updates the widget and sets the alarm again for the next second.

It is important to use an alarm type RTC_WAKEUPthat takes into account the system time and can wake the device from sleep mode. However, starting with Android 12 (API 31), the rules for background work have become stricter. Now, precise alarms may require special user permission.

In code, this looks like creating PendingIntent a unique code and passing it to the setExactAndAllowWhileIdlemethod. This ensures that the update will occur even if the device is in power saving mode Doze Mode.

Why canโ€™t Handler be used in widgets?

Widgets work in the context of remote views (RemoteViews), they do not have access to the UI of the main application thread directly and live separately from the Activity. Therefore, standard Activity Handlers do not work here; you need a system AlarmManager.

Optimization and work with TimeZone

When developing global applications, you cannot rely on the deviceโ€™s system time without taking into account the time zone. The user may be traveling and the watch should show local time or the time of the selected city. The class TimeZone allows you to get the offset relative to GMT.

If you are making a world clock, you will need a list of all available time zones. They can be obtained through TimeZone.getAvailableIDs(). When switching cities, it is necessary to recalculate the displayed time by adding or subtracting the resulting offset (offset) in milliseconds.

It is also worth taking into account the transition to daylight saving time. Modern versions of Android and the library java.time (available via desugaring for older versions) automatically handle these nuances if you use the right classes like ZonedDateTime.

โš ๏ธ Please note: Android APIs and background restrictions are constantly changing. Mechanisms that worked in Android 10 may be limited in Android 14. Always check the official developer documentation before release to ensure that the way you update the widget is not blocked by the power saving system.

Frequently asked questions (FAQ)

Why the widget is not updated every second?

Most likely, you are using standard updatePeriodMillis in XML, which is limited to 30 minutes. For a second update, you need to implement a mechanism with AlarmManager i PendingIntent, which will forcefully wake up the application.

How to make a clock with milliseconds?

The principle is the same as with seconds, but the update interval postDelayed or the alarm should be set for 10-50 ms. However, this will have an extremely negative impact on the battery. Updating the UI more than once per second for a clock is considered bad practice and is rarely used in real products.

Do you need special permission for the clock widget?

For the basic widget, special permissions not needed. However, if you plan to use Exact Alarms on Android 12 and above to update every second, the system may require the user to manually allow the app to set Exact Alarms in Settings.

Can Kotlin Coroutines be used instead of Handler?

Yes, you can use coroutines with in an Activity. data-i="149">inside the loop. This is a more modern and cleaner approach. But for widgets (AppWidgetProvider), coroutines are more difficult to use, since the life cycle of a widget is short, and the process can be killed by the system before the coroutine completes. delay(1000) inside the loop. This is a more modern and cleaner approach. But for widgets (AppWidgetProvider), coroutines are more difficult to use, since the widget's life cycle is short, and the process can be killed by the system before the coroutine completes.

๐Ÿ’ก

Creating a clock is a fundamental task that teaches you how to work with time, timers and the life cycle of Android applications.