Trimming Android XML Binary Resources to Shrink APK Size
This article explains the inner workings of Android's compiled XML format, demonstrates how to parse and manipulate its binary chunks, and presents a step‑by‑step optimization that removes namespaces and attribute names to reduce APK size by up to 28 % while handling compatibility issues.
Overview
This guide details a size‑reduction technique for Android layout resources that works directly on the compiled binary XML produced by AAPT2. By pruning unused namespace data and attribute names, and by shrinking string‑pool offsets, the binary size of layout files can be reduced significantly.
Binary XML Structure
Compiled XML is composed of a sequence of chunks . Each chunk begins with a ResChunk header containing type, headerSize and size. The main chunk types are:
StringPoolChunk – stores all string literals and style data.
XmlStartElementChunk – represents a start‑tag, holding namespace ID, name ID and an array of attribute descriptors.
ResourceMapChunk – a 32‑bit integer array that maps attribute IDs to positions in the string pool.
XmlNamespaceChunk – stores namespace prefixes and URIs.
Definitions are available in androidfw/ResourceTypes.h. For example, a StringPoolChunk header includes stringCount, styleCount, flags (UTF‑8 vs UTF‑16), stringsStart and stylesStart.
Parsing Example
A hex dump of a layout file shows how to read the header, string count, style count, flags and offsets. The fourth string ("textSize") is obtained by stringsStart + offset and decoding the UTF‑8 length fields.
Tooling
The open‑source library android‑chunk‑utils (https://github.com/madisp/android-chunk-utils) provides ready‑made chunk classes ( StringPoolChunk, XmlStartElementChunk, etc.) and a ResourceFile abstraction that can read a binary XML file, expose its chunks, and write a modified version.
Optimization Steps
Remove namespace chunks – filter out all XmlNamespaceChunk instances from the chunk map.
Clear attribute and namespace strings – replace every string in the StringPoolChunk that corresponds to a namespace URI or attribute name (e.g., http://schemas.android.com/apk/res/android, app, tools) with an empty string using stringPoolChunk.setString(i, "").
Shrink string‑pool offsets – when writing the file back, enable the shrink flag in ResourceFile.toByteArray(true). The library deduplicates identical strings and writes a compact offset table, collapsing many 00 00 00 entries into a single shared offset.
Sample Kotlin implementation:
FileInputStream(resourcesFile).use { input ->
val resource = ResourceFile.fromInputStream(input)
val chunks = resource.chunks
// Remove namespace chunks
val nsChunks = chunks.values.filter { it is XmlNamespaceChunk }
chunks.values.removeAll(nsChunks.toSet())
// Clear attribute and namespace strings
val strings = (resource.getChunk(StringPoolChunk::class.java) as StringPoolChunk).strings
strings.forEachIndexed { i, s ->
if (s in listOf(
"http://schemas.android.com/apk/res/android",
"http://schemas.android.com/apk/res-auto",
"http://schemas.android.com/tools",
"android",
"app",
"tools")) {
(resource.getChunk(StringPoolChunk::class.java) as StringPoolChunk).setString(i, "")
}
}
// Write with shrinking enabled
FileOutputStream(resourcesFile).use { out ->
out.write(resource.toByteArray(true))
}
}API Compatibility Adjustments
Removing attribute names can cause runtime crashes when code looks up attributes by name. The following patches illustrate safe handling:
TabLayout height – wrap TypedArray lookup in a try/catch and fall back to getInt() for android.R.attr.layout_height.
Image library src attribute – use obtainStyledAttributes() with hasValue() before calling getDrawable().
Toolbar theme – include both android.R.attr.theme and R.attr.theme in the attribute array.
int[] systemAttrs = {android.R.attr.layout_height};
TypedArray a = context.obtainStyledAttributes(attrs, systemAttrs);
try {
mHeight = a.getDimensionPixelSize(0, ViewGroup.LayoutParams.WRAP_CONTENT);
} catch (Exception e) {
mHeight = a.getInt(0, ViewGroup.LayoutParams.WRAP_CONTENT);
}
a.recycle();Results
Applying namespace removal and empty‑string shrinking reduced a sample layout from 596 bytes to 452 bytes (≈ 24 %). Enabling offset shrinking further reduced the total to 432 bytes (≈ 28 %). Across the whole APK, the cumulative saving was about 2.2 MB after repackaging.
Conclusion
The workflow demonstrates how to dissect Android’s compiled XML format, use android‑chunk‑utils to prune unused namespace and attribute data, and apply targeted compatibility fixes. The technique yields measurable APK size reductions while maintaining runtime stability.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
DeWu Technology
A platform for sharing and discussing tech knowledge, guiding you toward the cloud of technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
