Android apps are installed on devices with an enormous range of storage, network quality and screens. A large download is a conversion problem on slower connections and a deletion risk on low-storage phones. For many apps, drawable resources are the largest part of the package that you directly control: onboarding illustrations, backgrounds, icons in several densities and screenshots bundled for help screens. This guide walks through the practical order of operations to shrink the images and resources in an Android app, from measurement to automation.
Measure before you trust any percentage
Savings depend on your assets. Figures quoted in vendor blog posts are best cases. Record your baseline and measure each change.
Step 1: Measure with APK Analyzer
Android Studio includes the APK Analyzer. Open it from the Build menu by choosing Analyze APK, or drag a built APK or AAB into the editor. It shows a tree of the package with raw and download sizes for each folder and file. Expand the res folder and sort by size to find the heaviest drawables. Compare two builds with the compare option to see exactly what a change did.
Look at the download size, not just the raw size. Compressed formats shrink further when delivered, and the download size is what users experience. Also note how much of the package is code, native libraries and resources, so you do not spend a week on images when a large native library is the real culprit.
Step 2: Publish an Android App Bundle
If you still upload a universal APK, the first big win is to switch to an Android App Bundle, the AAB format that Google Play requires for new apps. With a bundle, Google Play generates optimized APKs for each device and delivers only the resources for that device's screen density, language and CPU architecture. A phone with an xxhdpi screen does not download the mdpi or xxxhdpi versions of your drawables.
This changes how you think about density folders. Providing one version per density no longer inflates every user's download, because they receive only their own. It does not remove the need to optimize, since each user still downloads the densities for their device, but it does remove a lot of waste.
Step 3: Remove unused resources
Unused resources are common in mature codebases, particularly after a redesign.
- Run Android Studio's lint check for unused resources and review the results.
- In your release build type, enable resource shrinking together with code shrinking, so the build removes resources that are not referenced. Resource shrinking relies on code shrinking being enabled, so configure them together and check the Android documentation for your Gradle plugin version.
- Use resource configurations to limit the languages and densities you package when not using a bundle.
- Delete duplicated images that exist under multiple names after refactors.
- Audit libraries. A dependency may bundle its own large drawables or sample assets.
Test a release build after enabling shrinking. Resources referenced only by reflection or by name at runtime can be removed incorrectly unless you configure a keep file.
Step 4: Prefer vector drawables for simple graphics
Vector drawables describe an image with paths rather than pixels, so one small XML file replaces a set of PNGs in several densities. They are ideal for icons, simple illustrations and shapes. Android Studio's Vector Asset tool can import SVG or choose from Material icons. Keep them simple: very complex vector paths can render slower and may be larger than a compressed raster image, so use rasters for detailed artwork.
Step 5: Convert raster images to WebP
For raster images that remain, WebP is usually smaller than PNG or JPEG at comparable quality, and Android supports it natively. Lossy WebP is supported from early Android versions, and lossless WebP with transparency is supported on API level 18 and above, which covers effectively every device you are likely to target now. Android Studio offers a Convert to WebP command on a drawable or a whole folder, and you can choose lossy or lossless options and a quality setting.
| Content | Recommended format | Notes |
|---|---|---|
| Simple icons and shapes | Vector drawable | Smallest and scalable |
| Photographs and rich backgrounds | Lossy WebP | Choose quality 75 to 85 as a starting point |
| Illustrations with transparency | Lossless WebP or quantized PNG | Compare sizes |
| Pixel-exact textures | Lossless WebP or PNG | Avoid lossy changes |
| 9-patch images | PNG | Stretching rules need the original structure |
Always inspect the converted images on a device. Test any asset that has gradients, fine lines or transparent edges.
Step 6: Ship the right density, and no more
Android uses density buckets such as mdpi, hdpi, xhdpi, xxhdpi and xxxhdpi, and the system chooses the closest match. Providing every bucket for every image multiplies size, and most modern phones fall into xhdpi, xxhdpi or xxxhdpi.
- For simple art, one vector drawable removes the issue entirely.
- For raster images, provide the densities you actually need. Many teams provide xhdpi and xxhdpi and let the system scale in the rare other case, then verify on a lower-density device.
- Use the nodpi folder for images that should never be scaled by density, such as a full-screen photo you size explicitly in code.
- Avoid shipping a 4000 pixel wide photograph in a layout that displays it at 400 dp.
Step 7: Resize before you compress
Pixel count dominates file size. Work out the largest place the image appears in dp, multiply by the density factor, which is 3 for xxhdpi and 4 for xxxhdpi, and export at that size. An image that is twice as wide and twice as tall as needed carries four times the pixels. Resizing first is the highest-impact step in the whole process, and it is free.
Step 8: Compress the sources
After resizing and choosing a format, compress what you will ship. This matters for PNG and JPEG files that remain, and for the sources you will convert to WebP, because starting from a smaller, cleaner source usually gives a smaller result.
- Run lossless optimization on every PNG. Pixels stay identical, and metadata and inefficiencies go.
- Run lossy quantization on flat illustrations, onboarding art and interface graphics, then compare at full size.
- Re-encode JPEGs with an optimized encoder such as MozJPEG.
- Do not repeatedly recompress. Keep originals and compress from them.
TinyPixels does all of this locally on macOS and Windows, using oxipng, imagequant and MozJPEG, with batch folders and the option to convert to WebP. Nothing is uploaded, which is useful for unreleased artwork, and Pro is a one-time purchase. See the TinyPixels guide for Android assets for a project-specific walkthrough.
Step 9: Handle big optional content separately
If your app has large optional content, such as high-resolution galleries, tutorial videos or extra level packs, consider Play Feature Delivery or Play Asset Delivery, which let you deliver parts of the app at install time, on demand or conditionally. Alternatively, download the content from a server on first use. These options add complexity, so use them for assets that are big and used by a minority of users.
Step 10: Automate and guard against regressions
App size creeps up when nobody is watching. A few habits keep it down.
- Set up a watch folder on the design export directory so new assets are compressed as soon as they land.
- Record APK or AAB size in your CI output for every build so changes are visible in pull requests.
- Add a size budget check that fails the build when the download size exceeds a threshold.
- Include an asset checklist in your pull request template: resized, correct format, compressed.
Checklist
- Baseline with APK Analyzer and note download size.
- Switch to Android App Bundle if you have not.
- Remove unused resources and duplicates, and enable resource shrinking.
- Replace simple graphics with vector drawables.
- Resize rasters to their real display size.
- Convert suitable rasters to WebP and compare on device.
- Compress remaining PNG and JPEG files.
- Move large optional content to on-demand delivery.
- Automate compression and track size in CI.
Common mistakes
- Judging progress by raw APK size instead of download size.
- Converting 9-patch images to WebP and breaking stretching behavior.
- Enabling resource shrinking without testing a release build and then losing a dynamically referenced image.
- Shipping huge photographs because they looked fine on a development phone.
- Using complex vector drawables for detailed artwork, making them slower and larger than a raster.
- Compressing an already compressed copy rather than the original.
Localization, density filters and other variants
Resources multiply with every language and density you support. If you publish a bundle, Google Play delivers only the language resources a user needs, so keeping many translations costs little. If you still ship universal APKs for a private distribution channel, restrict languages and densities with resource configurations so you do not package variants nobody will use.
Pay attention to images that contain text. A screenshot with English text baked in must be duplicated for each language, which multiplies size. Draw the text natively over a shared background image wherever possible, and you ship the background only once.
Keep a size budget
Choose a download size target for your release and record the figure from every build. A small script in continuous integration that prints the bundle size and fails above a threshold turns size from an occasional cleanup project into something the team sees on every pull request. Regressions then get fixed while the cause is still fresh in someone's mind.
Test on low-end and high-density devices
After optimizing, test on both ends of your supported range. A low-end device with limited memory shows decoding problems from oversized bitmaps quickly, and a high-density tablet reveals blurry upscaled artwork if you removed a density you actually needed. Include at least one of each in your release checklist, and compare screenshots against the originals so reviewers can see quality is unchanged where it matters.
Finally, remember that smaller images also decode faster and use less memory at runtime, so the work pays off beyond download size, in startup time and scrolling smoothness.
If you work with a design team, share the size budget with them. Designers who know the target weight of an onboarding illustration, and who export at the real display size in WebP or as vectors, remove most of the optimization work before developers ever see the files.
Conclusion
Reducing Android app size works best as a short, ordered process: measure, publish a bundle, delete what you do not use, prefer vectors, resize rasters, convert to WebP and compress what remains. Each step is small, and together they often make images stop being the heaviest part of the package. Keep the compression local and automated so the savings hold with every new asset.
Frequently asked questions
How do I see what is making my Android app large?
Open the APK or AAB in Android Studio's APK Analyzer. It shows raw and download sizes for each folder and file, so you can find the heaviest resources and compare builds.
Does an Android App Bundle reduce app size?
Yes. Google Play generates optimized APKs per device and delivers only the screen density, language and CPU resources that device needs, instead of everything in a universal APK.
Is WebP smaller than PNG on Android?
Usually, at comparable quality. Android supports WebP natively, and Android Studio can convert drawables. Compare sizes and test on a device, and keep 9-patch images as PNG.
Should I use vector drawables?
For simple icons and shapes, yes. One small XML file replaces multiple density-specific PNGs. Very detailed artwork is usually better as a raster image.
What does resource shrinking do?
It removes resources that the build can prove are not referenced. It works together with code shrinking, and you should test a release build because resources referenced only by name at runtime can be removed.
Can I automate image compression for an Android project?
Yes. A desktop tool with a watch folder, such as TinyPixels, can compress new images locally as they are added, without Gradle plugins.