Mobile Development 21 min read

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.

DeWu Technology
DeWu Technology
DeWu Technology
Trimming Android XML Binary Resources to Shrink APK Size

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AndroidKotlinAPK size reductionandroid-chunk-utilscompatibility patchesresource trimmingXML binary
DeWu Technology
Written by

DeWu Technology

A platform for sharing and discussing tech knowledge, guiding you toward the cloud of technology.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.