Dispatch Issue #21


GM fellow Compose wranglers and perpetual gradle prophets. This is JetpackCompose.app's Dispatch, rolling into your inbox with that "just discovered a performance optimization that actually works" energy.
This is Issue # 21 and it’s loaded with a ton of “you heard it here first” type of content. We also meet the team behind the very popular AI tool that many Android teams are already using and raving about — Firebender. So wear your reading glasses, turn on DND mode on Slack and tell your partners to stop giving you more errands to do because you are going to be busy for the next 10 minutes 🎧
🥂 Tipsy Tip
Your Easiest App Size Win of the Year is Here
Here's my favorite type of optimization: the kind that gives you meaningful improvements with almost zero effort. Google just dropped a gem called optimized resource shrinking that can shrink your app by 50%+ in some cases, and all it takes is adding one line to your gradle.properties
.
The magic happens because the new approach finally unifies code and resource optimization. Instead of the old two-step dance where AAPT2 generates overly conservative keep rules that prevent R8 from doing its job properly, the new pipeline lets R8 see the full picture
It can now trace references across the DEX-resource boundary and eliminate unused code AND the resources it references in one fell swoop.
Here's how to enable it:
// build.gradle.kts - make sure you have this already
android {
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
}
}
}
// gradle.properties - add this one line
android.r8.optimizedResourceShrinking=true
That's it! The improvements are particularly dramatic for apps that share significant resources across different form factors or feature modules. Even smaller apps see meaningful gains—Google's own Androidify demo showed consistent reductions in both DEX size and resource footprint ( over 50% in app size!!! ) .
Starting with AGP 9.0.0, this becomes the default behavior, but why wait? Enable it now and watch your APK sizes shrink while your users enjoy faster downloads and installations.
Skip the ‘Hello World’ and go straight to production! Bitdrift's new hands-on guide shows you how to wire up bitdrift in the real-world Wikipedia Android app, with custom logging, session workflows, and more. Read the guide
Take a look at this animation code:
@Composable
fun AnimatedBackground() {
// Here, assume animateColorBetween() is a function that
// swaps between two colors
val color by animateColorBetween(Color.Cyan, Color.Magenta)
Box(
Modifier
.fillMaxSize()
.background(color)
)
}
This looks innocent enough—but we could be doing something more efficient 🤔
See the answer in a section below ⬇️
😆 Dev Delight
If you figure out a way to improve build times of your app, I think it’s fair for you to say that you work on Climate Tech 🌤️
The afterlife ensures that you have closer access to the cloud 😅
🔦 Community Spotlight (and Subscriber Win)
Sometimes you discover a library and think, "Where has this been all my life?" That's exactly how I felt about Resaca , a Compose Multiplatform library that solves one of the most annoying limitations in Compose: ViewModel scoping.
What makes it extra special is that it’s built by long term reader of this newsletter and ardent supporter, Sebas , and I happen to randomly stumble on it.
Here's the problem it solves: Jetpack ViewModels can only be scoped to the activity or navigation graphs, but Compose encourages fine-grained, reusable components. This creates an architectural mismatch where you end up with massive screen-level ViewModels instead of focused, component-specific ones.
Resaca fixes this beautifully:
@Composable
fun FavoriteButton(itemId: String) {
// This ViewModel is scoped to THIS composable, not
// the whole screen
val viewModel: FavoriteViewModel = viewModelScoped(itemId) {
FavoriteViewModel(itemId)
}
Button(
onClick = { viewModel.toggleFavorite() }
) {
Icon(...)
}
}
The magic is that this ViewModel survives configuration changes, navigation to the backstack, and recomposition—but gets cleaned up when the composable is truly no longer needed. It's like having the best parts of remember
and viewModel()
combined.
The use cases are endless: dialog ViewModels that clean up when dismissed, LazyColumn items with their own business logic, reusable components with isolated state, and multi-pager screens with per-page ViewModels.
Even better, it supports dependency injection out of the box (Hilt and Koin extensions available) and works across all Compose Multiplatform targets. It’s also Chris Banes approved—high praise from one of the respected voices in AndroidDev.
If you've ever felt frustrated by ViewModel scoping limitations, give Resaca a try.
👩💻 Featured Developer
Meet Kevin Tang , co-creator of Firebender . We first connected last year, and he insisted on an in-person chat—then drove 45 minutes to make it happen. That’s the Firebender mindset—get close to users, sweat the details, and ship. Let’s dig in.
Give us the quick elevator pitch for Firebender – who should use it and what does it do for Android developers?
Firebender is the most powerful coding agent for android engineers, and now used by many fortune 500 companies for their teams. All the integrations, and agent design is focused for android tasks creating UIs from figma, iterating against gradle runs, and more.
Claude Code & Cursor are also popular choices for coding. How does Firebender fit into this landscape?
Claude Code performs well and is great for early adopters, but it is terminal based, which can feel really foreign to most android engineers. Firebender offers comparable performance while integrating directly into your Android workflows. Compared to Cursor, you don’t have to manage two IDEs or keep switching windows to test changes — Firebender stays put in your existing setup.
I was blown away when I saw Composer , an agent that turns Figma into Compose UI code. How do you think about design-to-code for Android?
Layout fidelity is genuinely hard, and most agents stumble on the last mile. Our approach is pragmatic: generate useful Compose scaffolds and patterns that get engineers 40–60% of the way there, then let humans refine. The priority is saving meaningful time while producing idiomatic, maintainable Compose code.
You’ve introduced “ background agents ” recently. What exactly can they do, and what was the trickiest part of getting them to work?
Background agents are a new class of coding agent that run continuously: they monitor the repo, propose diffs, and even create and execute their own to-do lists for tougher tasks. The hardest part has been balancing accuracy with speed — especially on large monorepos — which meant leaning on Gradle cache reuse and other performance-minded tricks.
What does a typical day look like for someone that works at Firebender? How big is the team?
We’re a small, hands-on team. Most days are a mix of building features, working closely with users, iterating on feedback, and imagining where the future of Android development should go — and then shipping toward it.
(L-R) Aman Gottumukkala, Zach Tang & Kevin Tang
You’re growing the team at Firebender – what kind of people would be a great fit?
Two traits matter most: hard work and exceptional engineering skills. Tools and languages can be learned; we’re looking for people who are hungry to build and excited about shaping the future of programming. If that sounds like you, reach out at kevin@firebender.com.
The issue is that color
is changing on every frame, and we're reading that state in the background()
modifier during the composition phase. This forces the entire Box to recompose on every single frame of the animation, which is expensive and unnecessary.
The fix:
Most folks are familiar with lambda based modifiers alternatives such as graphicLayer
and offset
for writing performant Compose code when the situation affords/requires it. However, I’m convinced that most Android devs aren’t familiar with the performant alternative for the background Modifier.
We can get the same effect in this context, without using the background
modifier itself. Here’s how we can defer the color reading to the draw phase:
@Composable
fun AnimatedBackgroundOptimized() {
val color by animateColorBetween(Color.Cyan, Color.Magenta)
Box(
Modifier
.fillMaxSize()
.drawBehind {
drawRect(color)
}
)
}
We can use the lambda variant of the drawBehind
Modifier which essentially ends up having the similar effect as the background
Modifier. Now when the color changes, Compose can skip composition and layout phases entirely, jumping straight to the draw phase ensuring that your animations will be buttery smooth 🥞
🦄 How you can help?
If you enjoy reading this newsletter and would like to keep Dispatch free, here are two quick ways to help:
👉🏻 Chip in via Github Sponsors . If your company offers an education budget, contributing a "coffee" to keep this newsletter going will certainly qualify . You'll get an instant receipt for reimbursement, and I'll keep Dispatch running ☕️
👉🏻 Spread the word . Tweet , Bluesky , toot, Slack, or carrier‑pigeon this issue to someone who loves Android. Each new subscriber pushes Dispatch closer to sustainability.
On that note, here's hoping that your bugs are minor and your compilations are error free.
