Arun Aditya
Menu
Android Command Line Tools Every Developer Should Know — Part 2

Android Command Line Tools Every Developer Should Know — Part 2

2026-06-10

AndroidAPKBuild Toolsaapt2apksignerbundletoolCLI

In Part 1 we covered the debugging side of the Android CLI toolkit — adb, logcat, dumpsys, bmgr, and sqlite3. Part 2 shifts to the build and release pipeline: the tools that compile your resources, inspect your APK, sign it, optimize it, and generate device-specific splits from an App Bundle.

These are the tools Gradle is orchestrating behind the scenes every time you hit "Run". Knowing them directly gives you far more control over what ends up in your APK and how it gets to users.


1. aapt2 — Android Asset Packaging Tool 2

aapt2 is the resource compiler at the heart of every Android build. It processes everything in your res/ folder, generates the R.java file with resource IDs, and packages resources into the APK. It replaced the original aapt with an incremental two-step model that rebuilds only what changed.

The two-step workflow

Step 1 — Compile: Each resource file is compiled independently into a .flat binary format.

aapt2 compile res/layout/activity_main.xml -o compiled/
aapt2 compile res/drawable/ic_launcher.png -o compiled/
# or compile the whole res/ folder at once:
aapt2 compile --dir res/ -o compiled/

Step 2 — Link: All compiled .flat files are linked together and packaged into an APK.

aapt2 link compiled/*.flat \
    -I $ANDROID_HOME/platforms/android-35/android.jar \
    --manifest AndroidManifest.xml \
    -o app.apk

The -I flag provides the platform android.jar so aapt2 can resolve framework resource references.

Inspecting an existing APK

# Dump all resources declared in the APK
aapt2 dump resources app.apk

# Print the binary AndroidManifest as readable XML
aapt2 dump xmltree app.apk --file AndroidManifest.xml

The xmltree command is particularly handy when you want to confirm what permissions, activities, or metadata ended up in the manifest of a release build without decompiling the whole APK.


2. apkanalyzer

apkanalyzer is the inspection tool for built APKs. You can use it to understand exactly what's contributing to your APK size, check the manifest, browse DEX methods, and verify resource contents — all without unzipping anything manually.

APK summary and size

apkanalyzer apk summary app.apk

Prints the package name, version code, and version name in one line. Useful in CI pipelines to verify the correct artifact is being processed.

apkanalyzer apk file-size app.apk

Breaks down the APK into its components (classes.dex, resources.arsc, lib/, assets/, etc.) with their download and disk sizes. This is how you find out what's actually making your APK large.

DEX method count

apkanalyzer dex methods app.apk

Lists every method in the DEX file. More importantly, you can count them:

apkanalyzer dex methods app.apk | wc -l

If you're approaching Android's 64K method reference limit, this is the command that tells you exactly where you stand — and which packages are contributing the most.

Manifest inspection

apkanalyzer manifest print app.apk

Prints the decoded AndroidManifest.xml. Unlike aapt2 dump xmltree, the output here is clean, human-readable XML rather than the raw attribute tree format.


3. apksigner

Every APK published to the Play Store must be signed, and apksigner is the tool for doing it. It supports all current Android signing schemes (v1 JAR signing, v2 APK Signature Scheme, v3, and v4) and is what Gradle uses under the hood during a release build.

Signing an APK

apksigner sign --ks my-release-key.jks \
    --ks-key-alias my-key-alias \
    app.apk

You'll be prompted for the keystore password. To pass it non-interactively in a CI environment:

apksigner sign --ks my-release-key.jks \
    --ks-key-alias my-key-alias \
    --ks-pass pass:$KS_PASSWORD \
    --key-pass pass:$KEY_PASSWORD \
    app.apk

Verifying a signature

apksigner verify app.apk

Exits with code 0 if valid, non-zero if not. Integrate this into your CI pipeline as a post-build sanity check.

apksigner verify --print-certs app.apk

Prints the signing certificate details:

Verified using v1 scheme (JAR signing): true
Verified using v2 scheme (APK Signature Scheme v2): true
Verified using v3 scheme (APK Signature Scheme v3): true
Signer #1 certificate DN: CN=Arun Aditya, O=..., C=IN
Signer #1 certificate SHA-256 digest: a1b2c3...

The SHA-256 digest is what you'd compare against Play Console to confirm you're signing with the right key before uploading.


4. zipalign

zipalign is a zip archive optimization tool that aligns uncompressed data in the APK to 4-byte boundaries. This allows the Android runtime to mmap resources directly from the APK file without copying them into memory first, resulting in lower memory usage and faster resource access.

Usage

zipalign -v 4 app-unsigned.apk app-aligned.apk
  • -v — verbose output, prints alignment status for each entry
  • 4 — align to 4-byte boundaries (always use 4 for APKs)

Verifying alignment

zipalign -c -v 4 app.apk

The -c flag checks alignment without modifying the file — useful to verify an existing APK.

The critical ordering rule

Always zipalign before apksigner, never after. Signing embeds a cryptographic hash of the APK's bytes. If you re-align after signing, you change those bytes and break the signature.

Gradle build
    ↓
zipalign       ← optimize first
    ↓
apksigner      ← sign last
    ↓
Upload to Play Store / distribute

Gradle's release build type handles this order automatically. You only need to think about it when running these tools manually.


5. bundletool

bundletool is the command-line counterpart to the Android App Bundle (.aab) format. When you upload an .aab to the Play Store, Google Play uses bundletool internally to generate optimized APKs for each device that downloads your app. With bundletool locally, you can simulate that process yourself — without uploading anything.

Build APK set from a bundle

bundletool build-apks \
    --bundle app.aab \
    --output app.apks \
    --ks my-release-key.jks \
    --ks-key-alias my-key-alias \
    --ks-pass pass:$KS_PASSWORD

This generates an .apks file (a zip of all possible APK splits) that you can then install or inspect.

Install on a connected device

bundletool install-apks --apks app.apks

bundletool queries the connected device's configuration (screen density, ABI, language, etc.) and installs only the splits that device actually needs — exactly as Play would.

Extract APKs to a directory

bundletool extract-apks \
    --apks app.apks \
    --output-dir extracted/ \
    --device-spec device-spec.json

You can generate a device-spec.json from a connected device:

bundletool get-device-spec --output device-spec.json

This is useful for auditing exactly which APK splits a specific device would receive.


The Complete Android Release Pipeline

Putting it all together, here's how these tools fit into the full build-to-publish pipeline:

Source Code
    ↓
Gradle compiles Kotlin/Java → DEX bytecode
    ↓
aapt2 compile     ← resource compilation
    ↓
aapt2 link        ← resource packaging
    ↓
APK assembled
    ↓
zipalign          ← memory alignment optimization
    ↓
apksigner         ← cryptographic signing
    ↓
apkanalyzer       ← size / method count inspection (optional CI check)
    ↓
Publish APK  OR  bundletool build-apks from .aab → Play Store

Most of the time Gradle handles all of this transparently. But when something breaks in your release pipeline — wrong signing key, bloated APK size, hitting the 64K method limit, a manifest permission that's not showing up — knowing which tool to reach for saves hours.


What's Coming in Part 3

Part 3 will cover bytecode compilation and migration tools: d8, jetifier, and retrace — the tools that sit between your source code and the DEX bytecode that actually runs on the device.