Zygote
APKs & installationintermediate

The resources system

Resources are the reason one APK can serve a hundred languages, five densities and two themes. The mechanism is a compiled table plus a fixed precedence order — and you can learn it by playing with it.

11 minAPI 1–37 · 17interactivequizverified 2026-10-27

Every Android app ships one resource system and uses it for everything: strings, images, layouts, colours, dimensions, styles. It is the mechanism that lets a single APK serve a hundred languages and five screen densities without a hundred APKs. Understanding it explains both a whole class of bugs and why App Bundles are possible.

TL;DR#

  • A resource is identified by an integer ID, not a file path. R.string.title is a constant.
  • The ID encodes package (0x7f), type, and entry index. That is why IDs are stable per build and why resource lookups are fast.
  • resources.arsc is the compiled table mapping IDs to values per configuration.
  • res/values-fr/, res/drawable-xxhdpi/ and friends are configuration variants, and the system chooses among them with a fixed precedence order.
  • RROs (runtime resource overlays) let a device replace another package's resources without rebuilding it — the mechanism OEMs use to theme Android without forking it.

Mental model#

Think of a library with one catalogue number per book, and several editions on the shelf.

The catalogue number is the resource ID. The editions are the configuration variants: the French edition, the large-print edition, the night-mode edition. A reader asks for catalogue number 0x7f0b0012 and the librarian picks the edition that fits them.

The library does not search the shelves by title. It looks up the number in a card index (resources.arsc) and takes the matching edition.

Where the analogy breaks#

A library has one reader at a time. Android has one process with one configuration at a time, but the configuration can change while the app is running — a rotation, a locale change, a night-mode switch — and the app is expected to re-read its resources. That is why Activity recreation exists.

The big picture#

Loading diagram…
Text description of this diagram

An interactive resource resolver. Choose a device locale, smallest width, night mode and density. Five steps show how Android narrows the candidate folders: first it eliminates any folder that names a value the device does not have, then it compares locale, then smallest width, then night mode, then density. The winning folder is shown for each configuration, together with the drawable that would be selected at that density.

Play with the configuration and watch which folder wins. The interesting cases are the ones where a folder is eliminated rather than merely outranked — values-sw720dp/ cannot serve a 600dp device at all, while values-fr/ merely loses to values-fr-rCA/.

Deep dive#

Resource IDs and why they start with 0x7f#

A resource ID is a 32-bit integer with three parts:

  • the package byte (for an app's own resources, 0x7f),
  • a type byte (string, drawable, layout, and so on),
  • an entry index.

The package byte is why 0x7f appears everywhere: it identifies the app's own resource package, while the framework's resources use 0x01. When you see 0x7f0b0012 in a crash log, you can decode it: the 0x7f package, a type, and an index into that type's table.1

The important consequence is that resource IDs are assigned at build time, not looked up by name at runtime. R.string.app_name is a compile-time constant. That is why resource lookups are cheap, and why a missing resource is a build error rather than a runtime one.

resources.arsc: the table#

resources.arsc is the compiled resource table. For each resource ID it holds a set of configurations and their values. A value can be:

  • a string (in a shared string pool),
  • a file path (for a drawable or layout),
  • a primitive (dimension, colour, integer),
  • a reference to another resource.

When the app calls getString(R.string.title), the framework:

  1. takes the ID,
  2. looks up the entry in the table,
  3. selects the best-matching configuration for the current device,
  4. reads the value.

No file system search, no XML parsing. That is why the design exists: the alternative — scanning res/values-fr/strings.xml at runtime — would be far too slow.

Configuration qualifiers and precedence#

A qualifier is the suffix on a folder: -fr, -xxhdpi, -night, -sw600dp. You can combine them: values-fr-sw600dp-night.

The matching algorithm has two phases:

Phase 1 — eliminate contradictions. If a folder names a configuration the device does not have, it is discarded. values-fr/ cannot serve an English device. values-sw720dp/ cannot serve a 600dp device, because the device's smallest width is smaller than the qualifier demands.

Phase 2 — compare survivors, in a fixed order. The order is defined by the platform and is roughly:

  1. MCC/MNC (carrier),
  2. locale (language, then region),
  3. layout direction,
  4. smallest width, then available width/height,
  5. screen size and aspect,
  6. round vs not-round,
  7. wide colour gamut, HDR,
  8. orientation,
  9. UI mode (night, car, desk, TV),
  10. density,
  11. touchscreen type,
  12. keyboard and navigation,
  13. platform version.

A folder that does not specify a qualifier is not eliminated at that step — it is simply deferred to a later one. That is why values/ is always a valid fallback: it names nothing, so it can never contradict, and it loses only when something more specific matches.

Density, and the scaling rule#

Density is special because it is the one qualifier that never eliminates a candidate. Every bitmap can be scaled. The platform prefers an exact density match, then the nearest density, and on a tie prefers the higher one — because downscaling looks better than upscaling.

nodpi is the exception: it means "do not scale this", so it is usable by any device and preferred when present for that resource.

AAPT and AAPT2#

The resource compiler is AAPT2. Its job:

  • compile each resource file into a binary form (.flat),
  • link all of them, plus the manifest, into resources.arsc and the final APK,
  • assign IDs,
  • generate the R class.

AAPT2 replaced AAPT largely for incremental builds: compiling each resource separately means changing one string does not recompile the world.1

Overlays: how OEMs change Android without forking it#

A Runtime Resource Overlay (RRO) replaces resources in a target package at runtime. Instead of modifying the target APK, the overlay is a separate package that declares "for package X, resource Y should have value Z".

RROs are how:

  • OEMs apply a theme or branding to AOSP apps without forking them,
  • a device can ship a regional variant of a resource,
  • an accessibility overlay can increase contrast or change sizing.

The overlay is enabled at runtime by the overlay manager, and the resource system resolves the overlay before the target. This is a Mainline-adjacent idea: it separates what the app is from how it looks on this device.2

Resources and App Bundles#

The configuration system is what makes App Bundles work. Because a resource is already tagged with the configurations it serves, Play can generate a split that contains only the density, ABI and language resources a device needs. Without qualifiers, every APK would have to contain everything.

That is the deep connection between this page and the next: the resource system is the reason per-device delivery is possible at all.3

How it evolved#

Architecture changes29 releases
APIVersionWhat changed
11.0
  • Initial AOSP platform: Linux kernel, Dalvik VM, core libraries
  • The first zygote-based app spawning model
41.6 Donut
  • Support for different screen densities via resource qualifiers
52.0 Eclair
  • First multi-core-friendly scheduling and graphics work
82.2 Froyo
  • Trace-based JIT compiler added to Dalvik
  • Support for x86 and ARMv7 optimisations
92.3 Gingerbread
  • Concurrent garbage collector added to Dalvik (GC pause reduction)
113.0 Honeycomb
  • Hardware acceleration for the View system (opt-in per app)
133.2 Honeycomb
  • New screen-size resource qualifiers for compatibility scaling
144.0 Ice Cream Sandwich
  • One system image for phones and tablets
  • Hardware acceleration on by default
164.1 Jelly Bean
  • Project Butter: vsync, triple buffering, Choreographer
  • RenderThread architecture begins
174.2 Jelly Bean
  • Multi-user support on tablets
  • SELinux enforcement begins in the kernel (behind the scenes)
184.3 Jelly Bean
  • SELinux enforcement rolled out more broadly
  • Binder changes for restricted profiles
194.4 KitKat
  • Android Runtime (ART) appears as an experimental runtime
  • Kernel moved under a tighter vendor control
204.4W KitKat (Wear)
  • First time the platform shipped for a new form factor
215.0 Lollipop
  • ART becomes the only runtime: ahead-of-time compilation at install
  • 64-bit ARM and x86 support
  • RenderThread and OpenGL ES 3.1
  • Project Volta and JobScheduler
  • SELinux fully enforcing
225.1 Lollipop
  • Device protection ties verified boot to Google accounts
236.0 Marshmallow
  • Runtime permission model changes how every app asks for access
  • Doze batches background work
  • Fingerprint HAL
  • Adoptable storage
247.0 Nougat
  • Project Treble groundwork
  • A/B seamless updates
  • Hybrid JIT + AOT compilation in ART
  • MediaServer split into sandboxed processes
268.0 Oreo
  • Project Treble: the vendor interface separates vendor and system code
  • Background execution limits
  • Broadcast and service restrictions
  • HIDL for HALs
278.1 Oreo
  • Android Go: a lighter configuration of the same platform
289 Pie
  • App Standby Buckets
  • Mainline groundwork
  • Non-SDK interface restrictions (greylists) begin
  • BiometricPrompt
2910
  • Project Mainline: APEX and updatable system modules
  • Scoped storage
  • Bubbles
  • Project Mainline starts shipping through Play
3011
  • GKI: the Generic Kernel Image splits the kernel into core + vendor modules
  • App hibernation
  • Package visibility filtering
  • Incremental app installs
3112
  • Bluetooth permissions split into the nearby-devices family
  • Exact alarm permissions
  • Foreground service launch restrictions
3212L
  • Window size classes and activity embedding
3313 Tiramisu
  • Granular media permissions replace READ_EXTERNAL_STORAGE
  • The notification permission becomes a runtime permission
  • Predictive back gesture
3414 Upside Down Cake
  • Foreground service types are mandatory
  • Selected photos access via READ_MEDIA_VISUAL_USER_SELECTED
3515 Vanilla Ice Cream
  • 16 KB page size support for native libraries
  • Edge-to-edge is enforced when targeting 35
3616 Baklava
  • The release moves to Q2 (June) with a Q4 feature drop
  • Two SDK releases per year starting with 36
  • Desktop windowing arrives in developer options then stable
3717 Cinnamon Bun
  • Strict per-device app memory limits are enforced by the system
  • Generational garbage collection is added to ART's Concurrent Mark-Compact collector
  • A lock-free MessageQueue for apps targeting API 37
  • Compose-first: new platform and Jetpack APIs target Compose only; View libraries enter maintenance mode
  • Hybrid APK signing (v3.2) combines classical and ML-DSA post-quantum signatures
  • Native library dynamic code loading must be read-only
2008–2017AAPTOne tool compiles and links; incremental builds are slow.API 1
2017 →AAPT2Per-resource compilation makes incremental builds viable.
2018 →RRO maturityOverlays become the standard way to theme AOSP without forking.
2021 →Split deliveryQualifiers become the basis for per-device resource delivery.API 30

Under the hood#

Curiosity corner#

  • Changing a resource ID is an ABI break. An app that caches IDs, or a split that references them, can break if IDs shift between builds.
  • 0x7f is not magic, it is a package index. The framework's own resources use 0x01.
  • A missing translation is not an error. If values-fr/ has no entry, the lookup falls back to values/ — which is why partially translated apps show a mix of languages.

Try it#

Read the resource table

aapt2 dump resources app-release.apk | head -60 aapt2 dump configurations app-release.apk adb shell cmd overlay list

Example output
Package name: com.example.app
type 0 configCount=4 entryCount=120
config fr  (values-fr)
config fr-rCA (values-fr-rCA)
config night (values-night)
config (default)

aapt2 dump resources prints the table the platform reads. cmd overlay list shows which RROs are installed and enabled on the device — often a longer list than people expect.

Build it#

Add a values-fr/strings.xml with one translated string, then switch the device language and watch the app fall back to English for every other string. That mixed state is the resource system working as designed.

Check yourself#

Check yourself

1 / 12

An APK is fundamentally which kind of file?

0 answered · 0 correct

Sources#

Sources

Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.

  1. docsApp resources overviewdeveloper.android.comaccessed 2026-10-27↩
  2. docsRuntime resource overlays (RROs)source.android.comaccessed 2026-10-27↩
  3. docsModular system componentssource.android.comaccessed 2026-10-27↩
  4. docsAPK signature schemesource.android.comaccessed 2026-10-27↩
  5. docsApp manifest overviewdeveloper.android.comaccessed 2026-10-27↩