Replicating the Standard Clock Android App With RxJava and Room | by David Adeyinka | Mar, 2022

Using the observable pattern in Android

Photo by Malvestida on Unsplash

When I was learning about streams and reactive programming, I found out about RxJava but put off learning it because it seemed complex.

I eventually decided to give it a go by creating an app where I easily found the Observable pattern, and so I chose to create a clone of the standard Android clock app.

I named the project “ClockClone.” My aim was to identify all the use cases of the standard clock app where the observer pattern was applicable and then recreate it myself using RxJava so I could learn it.

To keep things simple and to the point, I didn’t make the app a complete clone — not all the functionality was replicated. Amongst others, the alarms don’t actually work, and there are no fullscreen notifications.

The main features of the app are derived from the four tabs on the home screen of the Clock app:

  • Alarms — Set and manage alarms. There will only be persistent storage of alarm data, and no alarms will actually be scheduled.
  • World Clock — View time and weather conditions for major cities around the world.
  • Stopwatch — Run a stopwatch. It can also show split times and lap times.
  • Timer — Run a simple countdown timer.

I’ll only be discussing the parts of the app that are relevant to RxJava.

All the code can be found on Github here. The code in this article is shortened for brevity.

RxJava is the Java implementation of the ReactiveX API. To quote their homepage:

ReactiveX is a combination of the best ideas from
the Observer pattern, the Iterator pattern, and functional programming

All the operations in ReactiveX revolve around the use of Observables. They function similarly to Java Futures but are built to work with sequences of asynchronous values ​​instead of single ones.

Observables emit three main kinds of events: onNext, onComplete, and onError. The sequence of an Observable consists of zero or more onNext events and is terminated with either an onComplete or an onError event, but not both.

ReactiveX comes with a large set of operators that can be used to do a wide variety of things on Observables like transformations, filtering, combining Observables, aggregate operations, etc. I only used a few of them in the app, which I’ll discuss.

For the app, I’m using RxJava 3, specifically version 3.0.0. Check their Github releases page for the full version list.

You’re familiar with the following:

  • The Observer Pattern
  • Retrofit
  • Room
  • Android app architecture
  • Java Concurrency Framework
  • Dagger

The observable data needed for the stopwatch include the amount of time elapsed in milliseconds since starting — the run time, the amount of time elapsed in milliseconds since the last user clicked “lap” — the lap time, the state (OFF, RUNNING, PAUSED) — which is mainly for the UI and notifications, and the list of split and lap times, which will get updated every time “split” or “lap” is clicked. After coming up with that, I discovered that a special kind of Observable is needed to be able to manually emit events.

Subjects

Subjects act as observers of data and as Observables simultaneously. There are four kinds of Subjects:

  1. PublishSubject: emits values ​​to the Observer from the point of subscription. All values ​​emitted prior to subscription are never received.
  2. ReplaySubject: replays the stream from the beginning and emits new values ​​as well.
  3. BehaviorSubject: same as PublishSubject, but emits the most recent value before the subscription was made. A good example is a stream with an initial state.
  4. AsyncSubject: emits only the last value to the Observer. This value is only emitted when the Subject calls onComplete() .

I chose BehaviorSubject for the UI to be able to pick up the last state of the Observable when it restarts from the background or a configuration change.

To keep track of time and send updates at fixed intervals, I used ScheduledThreadPoolExecutor. It’s a better option than Handler since Handlers are executed on the main (UI) thread.

The timer works very similarly to the stopwatch. The observable data include the time left in milliseconds and the state (OFF, COUNTINGor PAUSED). I used BehaviorSubject and ScheduledThreadPoolExecutor as well.

The main sub-feature of the World Clock is the ability to see the current time of all the major cities around the world. The second sub-feature is the ability to see the summarised current weather conditions of these cities.

The observable data include a timer to update the current time being displayed, the weather conditions, and the list of cities. I’ll note that I had difficulties working on World Clock — I kept on changing the structure of the code till I found a class structure that worked well.

The central piece of data to this feature is the time zone identifier. It looks like this:

Africa/Lagos

It can be used to get the current time of the major cities as well as the weather conditions. I used data from the TZDB to create a list of time zones and stored it in the app assets as a CSV file.

The current time is getting from DateFormat which uses the time zone identifier to format it appropriately for the UI. The timer goes off every minute at the zeroth second to trigger a UI update.

The weather conditions are getting from network requests, which I’ll talk about shortly. The list of cities that the user has saved is stored as a simple comma-separated string.

I used SharedPreferences because it can signal observers whenever a change has been made and because a database would be overkill.

Weather Conditions

I chose to use AccuWeather to be able to work with Retrofit and try out the RxJava call adapter.

Creating a client interface with the adapter is done with a call to addCallAdapterFactory() on the Builder object.

A Scheduler can optionally be set for the adapter to use. Schedulers are used for multithreading. Check here for more information.

Single event Observables

The standard Observable is built to work with sequences of asynchronous tasks, but a network request is just a single task. While it’s possible to use Observable with Retrofit, there are special types in RxJava built to handle one-off events.

  • Single: emits either onSuccess or onError. onSuccess is emitted with a single item.
  • Completable: emits either onCompleted or onErrorbut no item is emitted with onCompleted.
  • Maybe: any one of the onSuccess, onCompleteor onError events will be emitted. onSuccess has a single item.

Cancelling subscriptions

While working on the weather conditions, I realized that it was important to be able to cancel network requests if the app is closed before they’re completed.

Network requests are a potential source of memory leaks, which in turn, could cause a crash. In ReactiveX, Subscribers are the special types that represent a subscription to an Observable, but in RxJava, they’re known as Disposables.

The Disposables will be used to cancel the requests when needed. They have a dispose() method that cancels subscriptions to Observables.

An Observable returns a Disposable object through a call to subscribe(). CompositeDisposables help to dispose of a set of Disposables all at once, similar to a forEach operation on a standard collection.

Update every minute

The interval observable emits an event with a useless item every minute to signal the UI to update the current time. To update the list every minute, I used the combineLatest() operator.

I decided to use a database to keep track of the alarms for the purpose of viewing, modifying, and deleting them, and so used Room with the RxJava adapter.

I found out from this article that Observable, Maybe, Single and Completable can be used as return types for a Room DAO method. In addition, Flowable can also be used.

Flowable is an Observable with support for backpressure, which is a situation where an Observable sends events faster than the Observer can handle them. An Observable can be converted to a Flowable using toFlowable() with a BackpressureStrategy as a parameter. The single event types like Maybe and Single don’t take a strategy as a parameter for conversion. I only use BackpressureStrategy.BUFFER in my code. Check out the links for more on Backpressure and Flowables.

Conversion to LiveData

All the Observable types can be converted into LiveData so they can be used with ViewModels. They have to be converted to a Flowable using which is passed to the LiveDataReactiveStreams.fromPublisher() method.

One thing I didn’t implement with LiveData is error handling. An error event from an Observable will cause the LiveData to throw a RuntimeException, which will cause an app crash.

It’s expected that you convert your possible errors into meaningful UI state objects before passing them to the LiveData. Check out this implementation from a Google sample.

I have some thoughts on RxJava. It’s a powerful library with a lot of potential, and I feel I barely scratched the surface for ClockClone. However, I did learn to put it to good use for the Observable pattern on Android.

I also like the fact that it isn’t exclusive to Android development, so it may come in handy in other areas. I’m relieved I finally got around to learning this. I hope you’ve learned something as well.

Leave a Comment