ANTIGRAVITY LABJP
Articles/App Development
App Development/2026-06-21Advanced

A Few Low-Density Phones Lost Their Bundled Wallpaper — The drawable vs nodpi Boundary in Play's Density Splits

App Bundle density splits will happily split images that should never be split, dropping a static resource on one density bucket only. Here is how I reproduced it with bundletool and fixed it by moving to drawable-nodpi or disabling density split — with the decision criteria.

Android13App Bundleindie development12Antigravity251AdMob10

Premium Article

While rolling an update out at 5%, a handful of reviews said the sample wallpapers "just show up gray, never load." I couldn't reproduce it on my Pixel or on a mid-tier test device. Crashlytics showed nothing. The one thing the reporters had in common: every device was a low-density budget phone. This was not a crash — a static image resource simply "did not exist" for one group of devices.

It took me a detour to find the cause. I blamed the CDN first, then resource shrinking from obfuscation, and finally landed on App Bundle density splits. As an indie developer who has run wallpaper apps for years, I thought I understood density splitting — but I had never run head-first into its real trap: it will split images that should never be split. This article walks from reproduction to fix, and shows exactly how far I let an Antigravity agent take the change, with the reasoning behind each call.

Why an image disappears "only on certain devices"

An Android App Bundle produces device-optimized split APKs from a single .aab. The split axes are ABI, language, and screen density. A device downloads only the splits matching its configuration: a low-density phone gets the low-density split, a high-density phone gets the high-density one, and resources for irrelevant densities never arrive. That is what keeps install size small.

The catch is how an image placed bare in res/drawable/ is treated. An unqualified drawable/ folder is treated internally as mdpi-equivalent, so with density splitting on it gets routed into the "medium-density split." A device whose density bucket does not pull that split can end up with no actual file behind R.drawable.sample_wallpaper. getDrawable() will sometimes substitute the nearest density, but when the target resource is in none of the density splits the device received, resolution fails outright. In my case the trigger was a single full-size sample wallpaper sitting in drawable/.

Resource locationDensity-split handlingReaches every device?
drawable/ (unqualified)Bundled into the mdpi splitNo (mostly mid-density devices)
drawable-xxhdpi/ etc.Bundled into each density splitNo (only that density)
drawable-nodpi/Excluded from density split (base split)Yes (all devices)
drawable-anydpi/ (vectors)Excluded from density splitYes (all devices)

Reproduce it with bundletool first

Fixing on a guess just moves the bug to another device. Before touching anything, I confirmed with my own eyes which density split holds what. With bundletool you specify a device spec and pull exactly the APK set that device would download.

# 1) Use the same .aab you released and write a device spec (JSON).
#    Set a low screenDensity to mimic a low-density phone.
cat > device-ldpi.json << 'JSON'
{
  "supportedAbis": ["arm64-v8a"],
  "supportedLocales": ["en-US"],
  "screenDensity": 240,
  "sdkVersion": 26
}
JSON
 
# 2) Generate only the APK set this device receives.
bundletool build-apks \
  --bundle=app-release.aab \
  --output=ldpi.apks \
  --device-spec=device-ldpi.json
 
# 3) Unpack the set and check whether the image is present.
unzip -o ldpi.apks -d ldpi_out
for apk in ldpi_out/splits/*.apk; do
  echo "== $apk =="
  unzip -l "$apk" | grep -i "sample_wallpaper" || echo "  (not present)"
done

Run it three times with screenDensity at 240 / 320 / 480 and you'll see sample_wallpaper appear in no split for one of those densities. The moment a vague "missing on some devices" turned into a concrete "absent from this density split," the fix chose itself.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Reproduce the 'image missing on certain phones' symptom yourself by pulling device-specific APK sets with bundletool
Tell apart images that should be split by density from images every device must receive, and choose between drawable-nodpi and disabling density split with confidence
Hand the raster-asset scan and move to an Antigravity agent while you keep the density judgment, and port that split of labor into your own repo
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

App Dev2026-06-01
Migrating Wallpaper Apps to Mandatory Edge-to-Edge on targetSdk 36 with Antigravity
The moment I bumped targetSdk to 36 (Android 16), the toolbar in my wallpaper preview screen slid under the status bar clock. Here is a working memo on handling the now-unavoidable edge-to-edge enforcement across several apps with Antigravity's agent.
App Dev2026-05-25
One Month Splitting Antigravity's Inline Edit and Agent Mode Across Four Wallpaper Apps
A month of notes from running Antigravity's Inline Edit and Agent Mode across four production wallpaper apps — with real counts, the decision rule I wrote into AGENTS.md, a one-pass dSYM fix, and how credit cost factors in.
App Dev2026-05-21
Tuning AdMob Placements at Runtime with Firebase Remote Config and an Antigravity Agent
A practical look at how I combine Firebase Remote Config with an Antigravity Agent to nudge AdMob placements while the app is live, drawn from years of running wallpaper apps as an indie developer.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →