Skip to content

ADFA-2306: Compress native libs in release APKs via legacy jniLibs packaging - #1527

Merged
davidschachterADFA merged 1 commit into
stagefrom
task/ADFA-2306-compress-native-libs
Jul 15, 2026
Merged

ADFA-2306: Compress native libs in release APKs via legacy jniLibs packaging#1527
davidschachterADFA merged 1 commit into
stagefrom
task/ADFA-2306-compress-native-libs

Conversation

@jomen-adfa

@jomen-adfa jomen-adfa commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Fixes ADFA-2306.

What

Release and instrumentation APKs now ship their native libs deflate-compressed: jniLibs.useLegacyPackaging = true is set per-variant behind the existing hasBundledAssets gate, so the installer extracts lib/<abi>/*.so to nativeLibraryDir at install time. Measured on v8 release: libs shrink 7.37 MB -> 1.44 MB (libtree-sitter-kotlin.so alone 4.18 MB -> 339 kB), final APK is 5.99 MB smaller. Debug APKs are byte-for-byte unaffected.

Why this approach

The ticket proposed brotli-compressing the libs into assets with app-side extraction. That was fully implemented and device-verified first, but measurement showed plain legacy packaging saves slightly more in aggregate (5.99 vs 5.60 MB) because deflate also covers libbrotli.so (1.08 MB -> 474 kB), which a brotli scheme must keep uncompressed as its own decompressor — with zero runtime code changes instead of ~17 modified files. The brotli implementation is preserved (patch + files) in lmr-automate/tmp/ADFA-2306-brotli-so-backup/; layering it on top later would net a further ~290 kB.

Details

  • recompressApk's release no-compress list now excludes "so" (noCompressRelease): the post-step previously forced .so entries back to STORED, silently undoing AGP's compression. Debug keeps "so" stored — debug loads libs directly from the APK, which requires stored entries.
  • Removed the dead module-level useLegacyPackaging = true in AndroidModuleConf.kt that app/build.gradle.kts always overrode.
  • libshizuku.so (an executable the adb shell runs from nativeLibraryDir) keeps working: install-time extraction still provides it there.
  • ARCHITECTURE.md documents the design and the recompressApk coupling.

Verification

On an arm64 emulator with the v8 release APK:

  • All 11 libs deflated in the APK, extractNativeLibs=true in the merged manifest; installer extracted them to nativeLibraryDir/arm64/ at install.
  • Clean launch, project open + Gradle sync, Kotlin tree-sitter highlighting rendered, terminal (local-socket) working, zero UnsatisfiedLinkError / crashes in logcat.
  • :testing:unit:test green; Spotless applied.

🤖 Generated with Claude Code

…ckaging

Release and instrumentation APKs now set jniLibs.useLegacyPackaging = true
per-variant (behind the existing hasBundledAssets gate), so lib/<abi>/*.so
ship deflate-compressed and the installer extracts them at install time.
Measured on v8 release: libs shrink 7.37 MB -> 1.44 MB, APK is 5.99 MB
smaller. Debug builds keep modern packaging (uncompressed, loaded straight
from the APK) via the false default in app/build.gradle.kts.

The release recompressApk post-step now uses a no-compress list without
"so" (noCompressRelease); it previously forced .so entries back to STORED,
which would silently undo the saving. Debug keeps "so" stored, as loading
libs directly from the APK requires it. Also removed the dead module-level
useLegacyPackaging = true in AndroidModuleConf.kt that app/build.gradle.kts
always overrode.

libshizuku.so still works: it is an executable the adb shell runs from
nativeLibraryDir, which install-time extraction continues to provide.

Verified on an arm64 emulator: installer-extracted libs in nativeLibraryDir,
clean launch, Kotlin tree-sitter highlighting, terminal, no link errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough
  • Compressed native libraries in release and instrumentation APKs, reducing the v8 release APK by 5.99 MB.
  • Enabled installation-time extraction for bundled-assets variants while preserving direct APK loading for debug builds.
  • Updated release APK recompression to include .so files.
  • Removed obsolete module-level packaging configuration and documented the behavior in ARCHITECTURE.md.
  • Verified installation, launch, project workflows, native linking, unit tests, and Spotless formatting.
  • Risk: release builds now depend on installation-time native-library extraction; debug and release packaging must retain their distinct compression settings.

Walkthrough

The build logic adds explicit v7/v8 ABI flavors, applies legacy JNI packaging to bundled-assets variants, and derives ABI version increments from variant names. Release APK recompression now excludes .so files, with corresponding build comments and architecture documentation.

Changes

Native library packaging

Layer / File(s) Summary
ABI flavor configuration
composite-builds/build-logic/plugins/src/main/java/.../AndroidModuleConf.kt
Non-:plugin-api modules define v7 and v8 flavors with matching ABI filters.
Variant packaging and versioning
composite-builds/build-logic/plugins/src/main/java/.../AndroidModuleConf.kt
Bundled-assets variants enable legacy JNI packaging, and APK version increments are selected from v7/v8 variant names.
Release recompression rules
app/build.gradle.kts, ARCHITECTURE.md
Release recompression uses a no-compress set without so, with matching documentation of debug and release packaging behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: daniel-adfa, hal-eisen-adfa, jatezzz

Poem

A rabbit hops through v7 and v8,
Packing little .so friends straight.
Debug stays swift, release compresses tight,
Flavors guide each APK’s flight.
Build notes bloom in docs tonight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: compressing native libs in release APKs via legacy jniLibs packaging.
Description check ✅ Passed The description directly describes the packaging change, rationale, and verification, all of which match the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task/ADFA-2306-compress-native-libs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
composite-builds/build-logic/plugins/src/main/java/com/itsaky/androidide/plugins/conf/AndroidModuleConf.kt (1)

174-179: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Prefer matching on variant.flavorName instead of a substring search on variant.name.

Using variant.name.contains can lead to false positives if other build types or future flavor dimensions accidentally contain "v7" or "v8" (e.g., a custom build type named v7Release). Since this logic determines the ABI version code increment, matching directly against the flavorName property is safer and more explicit.

♻️ Proposed refactor
-						val verCodeIncrement =
-							when {
-								variant.name.contains("v8", ignoreCase = true) -> flavorsAbis["arm64-v8a"]
-								variant.name.contains("v7", ignoreCase = true) -> flavorsAbis["armeabi-v7a"]
-								else -> 1
-							} ?: 1
+						val verCodeIncrement =
+							when (variant.flavorName) {
+								"v8" -> flavorsAbis["arm64-v8a"]
+								"v7" -> flavorsAbis["armeabi-v7a"]
+								else -> 1
+							} ?: 1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@composite-builds/build-logic/plugins/src/main/java/com/itsaky/androidide/plugins/conf/AndroidModuleConf.kt`
around lines 174 - 179, Update the ABI version-code selection in the
verCodeIncrement when expression to match against variant.flavorName rather than
substring-searching variant.name. Preserve the existing arm64-v8a, armeabi-v7a,
and fallback increment mappings while preventing build-type names from affecting
the result.
app/build.gradle.kts (1)

485-485: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Ensure the resulting collection is explicitly a Set to prevent a silent cast failure.

The recompressApk task casts the passed property to a Set<String> using as? Set<String> (around line 440). If the original noCompress collection is not a Set, the Kotlin minus operator (noCompress - "so") will yield a List. This would cause the as? Set<String> runtime cast to fail, silently falling back to an emptySet(), and unintentionally compressing files that must remain uncompressed (like .tflite).

Append .toSet() to guarantee compatibility with the expected type downstream.

🛡️ Proposed refactor
-val noCompressRelease = noCompress - "so"
+val noCompressRelease = (noCompress - "so").toSet()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/build.gradle.kts` at line 485, Update the noCompressRelease assignment to
convert the result of removing "so" from noCompress into a Set using toSet(),
ensuring compatibility with recompressApk’s Set<String> cast and preserving the
intended uncompressed entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/build.gradle.kts`:
- Line 485: Update the noCompressRelease assignment to convert the result of
removing "so" from noCompress into a Set using toSet(), ensuring compatibility
with recompressApk’s Set<String> cast and preserving the intended uncompressed
entries.

In
`@composite-builds/build-logic/plugins/src/main/java/com/itsaky/androidide/plugins/conf/AndroidModuleConf.kt`:
- Around line 174-179: Update the ABI version-code selection in the
verCodeIncrement when expression to match against variant.flavorName rather than
substring-searching variant.name. Preserve the existing arm64-v8a, armeabi-v7a,
and fallback increment mappings while preventing build-type names from affecting
the result.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3c60f1ab-4035-405d-93b3-60d199ec3aad

📥 Commits

Reviewing files that changed from the base of the PR and between ea12873 and 4bd01f7.

📒 Files selected for processing (3)
  • ARCHITECTURE.md
  • app/build.gradle.kts
  • composite-builds/build-logic/plugins/src/main/java/com/itsaky/androidide/plugins/conf/AndroidModuleConf.kt

@davidschachterADFA
davidschachterADFA merged commit eeffc0e into stage Jul 15, 2026
4 checks passed
@davidschachterADFA
davidschachterADFA deleted the task/ADFA-2306-compress-native-libs branch July 15, 2026 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants