Jetpack Compose... on the Desktop

The Awesome Android newsletter’s issue #201 pointed out a very interesting Gerrit entry related to Jetpack Compose, hinting at a future desktop offering. This possibility is something that I have been tracking for a few months now, so let’s explore what may be going on here.

Expecting Actual Code

When I was poring over the early dev builds of Jetpack Compose, I noticed that some elements had commented-out expect and actual keywords. For example, Canvas.kt had commented-out expect keywords and AndroidCanvas.kt had commented-out actual keywords.

Those keywords are tied to Kotlin/Multiplatform (KMP for short).

In KMP, you have Kotlin code that can run across all platforms (Kotlin/Common). That code can expect an API that, for any given platform, something else will supply the actual implementation of that API. For example, you can have common Kotlin code that will expect Android and iOS modules to implement the actual version of the expected API, using platform-specific stuff to do so. This is a bit reminiscent of how your main/ source set in an Android app can “expect” that each of your product flavors have an “actual” implementation of some class (e.g., a strategy pattern implementation tied to that flavor).

Since the keywords are commented out, we do not really need a KMP setup to be able to build or use Compose. And there are fairly few occurrences of these commented-out keywords, so this may have been just a form of documentation, leveraging KMP keywords for showing the relationship between functions.

But, somebody was thinking about Compose on multiple platforms, at least to some level.

Android as API

We often say that our code depends upon Android. However, much of the time, that is really shorthand for “our code depends upon Android APIs”. We use android classes and expect them to do what we want them to do. Exactly what those implementations are and how they accomplish their aims usually is beyond what we really care about as app developers.

As a result, as long as we satisfy the symbols that we reference and that the compiler needs, exactly what sits behind those symbols is immaterial, so long as our code works.

Android developers take advantage of that on a daily basis:

  • Your compileSdkVersion is used to identify a JAR containing all the public symbols from the Android SDK. But the actual implementation of all the Java methods is simply throw new RuntimeException("Stub!"). The real implementation of Android is added to our classpath at runtime; the android.jar that we compile against is not packaged in our APKs.

  • Robolectric, in the end, is an implementation of a slice of the Android SDK that does not have any Android ties. Robolectric’s implementation gets used in unit tests that run on the JVM, not on Android, allowing us to test a bit more of our code than we might otherwise, while also allowing us to avoid having to roll our own mocks of those android classes.

What That Gerrit Entry Shows

The main Gerrit link from the Awesome Android newsletter represents a KMP project, targeting the JVM desktop. In there, desktopMain/ is a source set for code that will be used for desktop apps.

And in that source set, we see class names that look an awful lot like a subset of the Android SDK, such as android.content.Context and android.view.View.

The implementations of those classes look nothing like Android. For one, they are all in Kotlin, and last I checked, the AOSP framework classes are still in Java. And those classes implement a tiny subset of the real framework APIs. Right now, the desktopMain/ implementation of Context, for example, is 43 lines of code… including the copyright header comment. There are individual methods in the real Context class that are longer than that.

The objective appears to be to satisfy the compiler to allow Compose code to build an Android app, with class and function implementations that use desktop Java instead of Android.

The actual UI is rendered by a SkiaWindow, which relies on JetBrains’ Skija project, which offers Skia bindings for Java. Skia is an open source 2D graphics library, one that powers rendering in Android, Chrome OS, and in browsers like Chrome and Firefox.

So, at least at an experimental level, Google appears to be working on getting Jetpack Compose to support desktop apps, as well as Android apps.

Implementing “fake” Android framework classes may be somewhat of a stop-gap. If Google elects to take Jetpack Compose more fully into KMP, uncommenting those expect and actual keywords and expanding upon them, then the desktop code would switch to providing the actual API that Compose is trying to expect.

Of course, right now we have no idea if Google will ever bring this to fruition. For all we know, this is just an experiment, one that will never see the light of day (beyond blog posts like this one). We know that it is actively being worked on — one of the developers uploaded a new patchset while I was writing this post.

Regardless, implementing something like this is likely to happen. I have seen other developers start to poke at this sort of thing, and I considered doing it myself. It’s possible that all of those efforts will come to naught as well, but there’s a decent chance that somebody will succeed.

If Jetpack Compose becomes Google’s recommended UI approach for Android, having the ability to also develop desktop apps from much of the same code base is a nice value-add, at minimum… and possibly much more.