Smaller apps feel faster, install more reliably on spotty networks, and respect the limited storage on real devices. In our latest Android release for Gratitude we reduced the install size by 30%. The work focused on practical changes that ship immediately rather than large refactors.
If you are planning a similar effort, start by establishing a baseline and a clear target. We treated size as a product metric alongside cold start, crash rate and ANR rate. That mindset helped us prioritize decisions and kept the changes safe to land.
How we measured
Before changing anything we set a baseline. Here’s a simple way you can measure your app size:
Build a signed release
Go to Build → Generate Signed Bundle or APK, choose Android App Bundle or APK, and use your release signing config.Open in Apk Analyzer
Drag the generated .aab or .apk from Finder or File Explorer into Android Studio.
Alternatively, use Build → Analyze APK.Check the key numbers
The Analyzer shows Raw File Size at the top, plus a breakdown of Download Size for each folder and file. Expand the base module to inspect res, dex, and lib.These are usually the heaviest parts of an app. The DEX folder reveals method counts, while res and assets show which resources take up the most space.
Rebuild and re-analyze after each change to see what really moves the needle.
Resource optimizations: WebP over PNG
We had already been using WebP for many of our images, but a lot of older assets were still in PNG. As part of this effort we migrated every remaining PNG where it made sense to WebP.
WebP is usually smaller while keeping quality high. For most images we used lossy WebP with a quality setting around 75–85, which reduced file size significantly without visible degradation. For assets where pixel-perfect edges were important we used lossless WebP. Nine-patch images stayed as PNG since WebP doesn’t support them.
Android Studio makes this migration easy: right-click a drawable folder → Convert to WebP → choose lossy or lossless → preview before saving. For batch conversion you can also use the cwebp command-line tool:
cwebp input.png -q 80 -o output.webp
After converting the leftover PNGs, we rebuilt the release bundle and verified the size reduction in the APK Analyzer.
In addition, we already save a lot of asset size cost by using vector drawables wherever possible instead of raster images. For resources that are not needed immediately at app startup, we host them remotely and load them on demand instead of shipping them in the bundle. These practices further helped us keep the packaged size lean.
Removed unused dependencies
Another win came from cleaning up old libraries that were no longer necessary. Over time, our project had accumulated dependencies that solved problems Kotlin and the Android framework already handle today. Removing them reduced method count, build time, and final app size.
RxJava → Coroutines
We had already been using Kotlin Coroutines in most of the app, but some older screens were still using RxJava. As part of this effort, we migrated the remaining Rx code to Coroutines and Flows. This let us finally drop both rxjava and rxandroid from our dependencies.
ButterKnife → View Binding
Similarly, while most new screens already used View Binding, some legacy code still relied on ButterKnife. We migrated those remaining screens to View Binding and removed ButterKnife entirely.
Compose Icons → Vector Drawables
We also removed Jetpack Compose’s icons library. Instead of bundling the entire set, we now include only the icons we actually need as SVG vector drawables. This keeps the app size smaller and aligns with Android’s recommended approach.
Other cleanups
In addition to the major libraries above, we also dropped a handful of old and unused dependencies that were no longer relevant to the project. Each removal not only shaved off a few kilobytes, but also reduced R8’s workload and simplified our dependency graph.
Together, these dependency cleanups gave us a noticeable reduction in size while making the codebase more modern and maintainable.
R8 full mode and ProGuard rules
We had already been using R8 for code shrinking and optimization, but in earlier builds we disabled full mode because of some runtime issues. Over time we cleaned up our ProGuard rules, removed reflection-heavy libraries, and solved the root causes. Once we were confident, we re-enabled full mode with:
android.enableR8.fullMode=true
On newer Android Gradle Plugin versions R8 may already run in full mode, but we keep this flag in our gradle.properties so the intent is clear in version history.
A big part of making R8 effective was optimizing our ProGuard rules. Over the years the rules file had grown with a lot of legacy -keep entries. Many of these were tied to libraries we no longer use. By auditing and removing them, R8 was able to be more aggressive in stripping unused code and resources.
With full mode enabled and clean rules in place, R8 did its job exactly as intended: shrinking the DEX size, removing unused classes, and making our build artifacts noticeably lighter.
Optimized resource shrinking
Resources (drawables, layouts, translations) can contribute a lot to app size. Standard resource shrinking already removes unused resources when you enable shrinkResources = true, but AGP 8.12.0 introduced a new optimized resource shrinking pipeline that integrates resource and code analysis for even better results.
On AGP versions before 9.0.0, you enable it manually by adding to gradle.propertiesandroid.r8.optimizedResourceShrinking=trueOn AGP 9.0.0 and newer, optimized resource shrinking is applied automatically whenever shrinkResources = true is enabled, so the flag is no longer needed.
After turning this on, R8 and AAPT together were able to strip out unused drawables, selectors, layouts, and translations more aggressively. For us, the savings were incremental compared to larger changes like WebP migration or dependency cleanup, but combined they pushed us closer to the overall 30% reduction.
By combining resource optimizations, dependency cleanup, R8 full mode, and optimized resource shrinking, we reduced Gratitude’s Android app size by 30%. This wasn’t the result of one big change but rather a series of practical improvements that worked well together:
Migrating all old PNGs to WebP, while continuing to rely on vectors and hosted resources where possible.Removing unused dependencies like RxJava, ButterKnife, and Compose’s icon library, while finalizing the migration to Coroutines, View Binding, and SVG vectors.Enabling R8 full mode after resolving earlier runtime issues, and cleaning up ProGuard rules so the shrinker could be more effective.Enabling optimized resource shrinking in AGP to strip unused assets more aggressively.
The outcome is a leaner build that installs faster, consumes less storage, and improves the overall user experience. Just as importantly, we now treat app size as a product metric, alongside cold start, crash rate, and ANR rate. That mindset ensures the wins stick, because size regressions are visible and actionable.
There’s still plenty of room to push further, for example, automating size reporting and analysis in CI/CD pipelines would make bundle size tracking part of our regular development feedback loop.


Recent Comments