Building Reactive Apps on JavaFX

Because 'reactive' isn't just a buzz word.

I come from a web development background, professionally. You could call that my first love, really, but sometimes you have a problem that can only be solved with a traditional thick client. That was the situation I found myself in when I dreamt up Animation Viewer: a tool for previewing the sprite animations I was drawing. (You can check out the demo video below and browse the source code on GitHub. The java-8 branch is the latest and greatest.)

I've dabbled with Swing and JavaFX before, but it always leaves me feeling uneasy. All these UI components wind up being tightly coupled in ways we'd never see in web-land. Well, thanks to ReactiveX and Akka, I think I've finally come up with a set of tools that solves this problem cleanly, and that's what I want to share with you here.

Quick confession: the code is actually in Scala using the JavaFX wrapper, ScalaFX. The general approach should still be applicable to plain Java, it just won't be as concise.

Making state your friend

Application UI is one of those areas that drives functional programming afficionados nuts. So much mutable state! There are books and blogs full of sophisticated techniques for "functionalizing" UIs, as well as UI frameworks written from the ground up to be that way. As someone who buys-into the benefits of functional design, that's really cool, but I'm going to go out on a limb and say it's not always a very approachable goal. What I'm presenting here is not an attempt to fight the statefulness of JavaFX's UI code, but to manage it. Instead of having to hop on the back of that bucking bull, we're going to lasso it and make it behave. Yeehaw!

Let's consider the Frames-Per-Second (FPS) control from Animation Viewer. This value controls the speed of the animation playback. It's actually three seperate UI elements tied together.

Animation Viewer UI with the relevant elements highlighted.

  1. a text field displays the current value and allows you to type in the desired frame rate,
  2. an up arrow button, and
  3. a down arrow button

The buttons bump the FPS up and down by one. We need to correctly handle the user's input to any of those controls and, when the FPS changes, inform the canvas so it can adjust the playback speed accordingly.

Assume we make our text field responsible for keeping the "current" value of our FPS (in order to avoid a mutable singleton) and that we employ a basic JavaFX approach. Our up button's sequence diagram might look like this:

Basic

Retrieve the FPS value, increment it, and tell the text field and canvas to update. Simple enough on its own. But keep in mind this logic has to be duplicated with a slight alteration for the down button, and we still have to write code to handle typing input into the text field. That feels like we're repeating ourselves.

I see two more problems with this approach. First, it's no longer thread-safe because we have multiple points of responsibility. Imagine if I could click the up and down arrow at the same time. Will the canvas update to 13 or 11? Will it match what the text field displays? Personally I hate debugging race conditions, so I would rather my architecture make them impossible.

Second, my button needs a reference to both the text field and the canvas. This tightly couples the code and creates a cross-reference spaghetti that makes me crazy. If you provide those references in the constructor, you now have to be careful about the order in which you create objects. (And good luck if your dependency graph contains a cycle.) If you provide references with a setter post-construction, you've made it possible for your objects to exist in an invalid state. You could easily forget to set an important reference. (This is probably what Jeff Goldblum did in The Fly.)

Classic Jeff.

Again: I'd rather the architecture make these sorts of problems disappear. This is where ReactiveX comes in.

Getting reactive

Take a step back and forget about the UI controls. Think about the signals which your application needs to run. The FPS value is a signal. It starts at some default value and changes over time. We want it to change in response to certain conditions: thus it is an Observer. When it changes we want to take action: thus it is Observable. These two patterns define the crux of ReactiveX; from their docs:

The ReactiveX Observable model allows you to treat streams of asynchronous events with the same sort of simple, composable operations that you use for collections of data items like arrays. It frees you from tangled webs of callbacks, and thereby makes your code more readable and less prone to bugs.

It also frees you from manually dealing with asynchronous inputs, and I've used it to decouple, de-duplicate, and simplify my code. This is a very thin intro for a rich piece of tech, so I encourage you to delve into their documentation (which is really quite good) for more. I'm just aiming to provide a real-world example, so I will gloss over a lot of detail. Simply put: we define and update observables and, in order to do something when they change, we subscribe to them.

ReactiveX also provides a concept called Subject which simply combines Observable and Observer into a convenient package. It's a little controversial as to when you should use Subjects, generally, but I think they're both warranted and low-risk in this case.

FPS as Subject

Let's return to our example. As mentioned we can think of our FPS value as a signal that changes over time. In RxScala (the Scala implementation of ReactiveX), we might define it like this:

val fps: Subject[Double] = SerializedSubject(BehaviorSubject(12.0))

A SerializedSubject is simply one that we can update from multiple threads without breaking it. It wraps another Subject and handles proper synchronization so we don't have to.

A BehaviorSubject is a Subject with a default value. When we subscribe to a BehaviorSubject we will immediately get one value: the most recent value, or the default if no other values have been published. This is perfect, since FPS should never have no value nor, God forbid, null.

Updating the FPS value is as simple as calling:

fps.onNext(value)

What about our up and down buttons? We can start by converting button clicks into their own Subjects. I call these fpsUp for up arrow clicks and fpsDown for down arrow clicks. These Subjects don't need a value, but instead use a singleton object I've called Impulse. Think of it as an event with no data payload. The information is simply that something happened. Defined like this:

val fpsUp: Subject[Impulse.type] = SerializedSubject(Subject())

There's no default value, of course, so it's not a BehaviorSubject, but we still want that serialization protection.

Now our button could be simply this:

val upButton = new Button("Up") {
  this.onAction = () => fpsUp.onNext(Impulse)
}

Every time the button is clicked, fpsUp is triggered. How does that increment the value? Our original approach made the text field the responsible entity. Even though the FPS value has moved to the fps Subject, let's keep our logic in the text field. So it will subscribe to our button-click Subjects and handle them.

val textField = new TextField {
  ...

  fpsUp  .subscribe(_ => runLater { text = (myFps + 1).toString })
  fpsDown.subscribe(_ => runLater { text = (myFps - 1).toString })

  text.onChange((obsval, prev, now) => now match {
    case fpsFormat(x) => fps.onNext(x.toDouble)
    case _            => text = prev
  })
}

This one's lengthy, so let's break it down. First the text field subscribes to fpsUp. When it gets an update, we increment the text property of the text field. Platform.runLater is a JavaFX detail: updates to UI elements have to happen on a certain thread and this makes that happen.

There's a similar subscription for fpsDown.

Next, whenever the text property changes (after a validity check), we push that value to the fps Subject. This gracefully handles both typed input and our button clicks. And that's it!

I've left out two definitions for brevity: myFps extracts an integer from the TextField's text property, and fpsFormat is a regular expression guaranteeing that x is a valid integer 1-99 with no leading 0. If you're unfamiliar with Scala's match syntax, let it suffice to say that you could easily replace it with an if-else block involving a regular expression check. If valid, send the new FPS value, else revert back to the previous value. This effectively stops the user from typing in gibberish like "q".

To recap: we've combined our three controls to output a single, safe, value signal. Also notice that none of the controls needs a reference to the other or the canvas to do its job. No one needs to care if one, zero, or a thousand different objects are watching their output signals. We've effectively and efficiently "event-ized" the FPS setting for the entire application.

A caveat worth mentioning: I'm using the text property of the TextField as a convenient proxy for the current value of fps. This is safe if and only if I guarantee the two are always in sync. In other words, this TextField has to be the sole entity that ever calls fps.onNext. (Or I could take pains to listen for changes from outside.) It's risky design, and part of the reason why using Subjects is controversial. However I'm using it as a shortcut knowing that this is a small application. In a large app, you should take extra steps to protect your signals, exposing them only as Observables except as needed. (Remember a Subject implements both the Observable and the Observer interfaces.)

Using the FPS signal

So how does our canvas know when the FPS changes? It can subscribe to the observable by doing something like:

fps.subscribe( ... )

where that ... is some function Double => Unit, or in Java terms, a void function with a single Double argument. If you peruse the source code, you'll notice that canvas' subscription is actually a little more complex than this. I'll probably cover canvas in detail in another article, but for now I just want to take one step further.

If you rapidly click the up or down buttons, the FPS signal changes rapidly too. What if changing the playback rate in the canvas is actually sort of computationally expensive? (Spoiler: it is here!) Ideally the app should wait for the FPS value to stabilize somewhat before doing the work. With ReactiveX this is trivial. You can create a new "debounced" signal from our base FPS signal:

val fpsDebounced: Observable[Double] = fps.debounce(500 milliseconds)

If the canvas subscribes to that instead, it will be updated at most every 500 milliseconds, saving some work when the signal is rapidly changing.

ReactiveX is full of these Observable operators (and there's nothing stopping you from creating your own) that make rather complex functions almost trivial to implement. Besides decoupling elements, this is the other major value-added.

Other signals

Converting UI actions into a simple value is all well and good, but most applications are built from more beefy requirements than this. Let's take a look at implementing a more involved, core workflow in Part 2.