Reliable Hot Reload on Android

What if I told you that there was a way to get reliable hot reload on Android with zero compiler hacks using Android APIs that have been available since 1.0? While this trick doesn’t allow you to introduce new code, it does let you invoke a lambda with arguments from the command line and without recompiling or relaunching your app.

Turns out it’s pretty easy to trigger a function in your app with a locally registered BroadcastReceiver that can be invoked with an adb shell am broadcast. Let’s say you have a simple screen with a square in the middle like this:

A really complex Android app

Now, you need to tweak some values such as the square’s color, size, and rotation. Normally, you would have to change the code, recompile, reinstall your app, and then navigate back to your feature. For small apps, this is fine, but for more complex ones, it could be minutes before you are able to see your change. Let’s make this process faster.

First, let’s create a simple BroadcastReceiver that registers itself and calls a lambda whenever it is invoked:

class HotReloadReceiver(
context: Context,
intentAction: String = "debug",
private val receiver: (Intent) -> Unit
) : BroadcastReceiver() {

init {
context.registerReceiver(this, IntentFilter(intentAction))

override fun onReceive(context: Context, intent: Intent) {

Second, let’s register a HotReloadReceiver in our app:

override fun onStart() {
HotReloadReceiver(this) {
val (r, g, b) = intent.getStringExtra("color")
val color = Color.argb(255, r, g, b)
rectangleView.background = ColorDrawable(color)

Lastly, all we have to do is run:

adb shell am broadcast -a debug --es color 0,0,255

to instantly turn the rectangle blue! Unlike recompiling your code, this change is made without affecting the rest of the app. Here is the demo repo in action:

Hot Reload in action!

You should not merge this code because it could expose your app to unintended manipulation from users and it doesn’t unregister itself. However, you could modify this so that it is only available in development builds and then share common debug options or utilities with your team. If you do that, consider creating a set of scripts or bash aliases to simplify things.

While this method can’t be used to add new code, it is still useful for a wide variety of situations. It can be used to update state properties, simulate error or loading states, send MVI intents, tweak animation timing, colors, sizes, set preferences, update feature flags, simulate NFC taps, and countless other things.

At Tonal (we’re hiring), I have used this to simulate weight lifting reps and to tweak animation timing. However, we just started using this and I’m sure we’ll find more benefits as time goes on.

Let me know in the comments or on Twitter if you find this useful or come up with new and creative use cases!

📝 Read this story later in Journal.

🗞 Wake up every Sunday morning to the week’s most noteworthy Tech stories, opinions, and news waiting in your inbox: Get the noteworthy newsletter >