docs: DEX optimization plan (strip proguard blanket keeps, DEX -43.9%)

This commit is contained in:
andrew committed 2026-09-29 14:42:50 +07:00
1 parent d0ca1be15e
commit 84eea056c6
1 file changed
+143
@@ -0,0 +1,143 @@
# DEX Optimization (Play Console metrics) Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Raise Play Console DEX metrics (shrink/obfuscation/optimization currently 40-42%, level Medium) by removing redundant blanket ProGuard keep rules, then verify the app still works end-to-end on a physical device.
**Architecture:** Flutter Android app whose DEX (1.1MB, 1165 classes) is ~40% pinned by legacy `io.flutter.** { *; }` blanket keep rules. Flutter's built-in `flutter_proguard_rules.pro` (auto-applied from the Flutter SDK gradle package) already keeps `FlutterPlugin` implementations with `allowobfuscation`, plus manifest keeps and `@Keep` annotations cover the rest. Strip the redundant rules, add standard R8 hardening flags, rebuild, verify DEX size reduction, smoke-test on a physical device (Redmi A5 over adb/usbipd), then commit.
**Tech Stack:** Flutter 3.47.4, AGP 9.0.0, Gradle 9.1.0, R8 (full mode default), Android SDK 36, adb via usbipd WSL passthrough.
**Spec:** In-conversation analysis (2026-09-17) — Play Console reported: DEX code optimization Medium, optimization 40%, obfuscation 42%, shrinking 41%. Root cause: blanket keep rules. Expected outcome: shrink/obfuscation ~65-75%, never 100% (remaining DEX is genuinely used plugin code). Baseline-profile work explicitly OUT OF SCOPE.
## Global Constraints
- Working dir: `/mnt/d/Flutter_Projects/TimbanganSiab` (git branch `main`; user explicitly authorized committing directly to `main`)
- Release build command (exact): `flutter build appbundle --release --obfuscate --split-debug-info=build/debug-info --dart-define=SIAB_DEVICE_NAME=SIAB`
- `flutter install` takes NO `--dart-define` flag; build APK with the flag first, then `adb install -r`
- PATH required: `export PATH="/home/ifitworks/flutter/bin:/mnt/d/android-sdk/platform-tools:$PATH"`
- Device: Redmi A5 (serial `863d0058304831323851054b1c83ab`), app id `id.ptsemangat.siab_scale`, installed copy is signed with release key
- If device missing from `adb devices`: `"/mnt/c/Program Files/usbipd-win/usbipd.exe" attach --wsl --busid 1-2`
- Baseline DEX size (pre-change): `classes.dex` = 1097572 bytes
- No new dependencies. No `git push` (user runs push themselves).
- Do NOT touch: `gradle.properties` migrator flags (`android.builtInKotlin=false`, `android.newDsl=false`), settings.gradle.kts versions, workflow file.
---
### Task 1: Strip redundant ProGuard keep rules
**Files:**
- Modify: `android/app/proguard-rules.pro` (full rewrite)
**Interfaces:**
- Consumes: nothing (config file only)
- Produces: rules consumed by R8 during release build in Task 2
- [ ] **Step 1: Replace entire contents of `android/app/proguard-rules.pro` with:**
```proguard
# Play Core deferred components
-dontwarn com.google.android.play.core.**
-dontwarn com.google.android.play.core.splitcompat.**
-dontwarn com.google.android.play.core.splitinstall.**
-dontwarn com.google.android.play.core.tasks.**
# Aggressive R8 optimization (missing from default configs)
-allowaccessmodification
-repackageclasses ''
```
Removed (verified redundant): all 5 `io.flutter.**` keep lines (Flutter SDK auto-applies `flutter_proguard_rules.pro` which keeps `FlutterPlugin` implementors with `allowobfuscation`; JNI natives use `@Keep`; app methods are `@Keep`d by GeneratedPluginRegistrant), `-keep class id.ptsemangat.siab_scale.** { *; }` (manifest keeps `MainActivity`, `@Keep` on `GeneratedPluginRegistrant`), `-keep class com.lib.flutter_blue_plus.**`, `-keep class com.tekartik.sqflite.**` (both instantiated statically in `GeneratedPluginRegistrant` + covered by Flutter's FlutterPlugin rule).
- [ ] **Step 2: Commit**
```bash
git add android/app/proguard-rules.pro
git commit -m "perf: strip redundant blanket proguard keeps for DEX shrink/obfuscation"
```
---
### Task 2: Rebuild AAB and verify R8 output
**Files:**
- Create/replace: `build/app/outputs/bundle/release/app-release.aab`, `build/app/outputs/mapping/release/mapping.txt`
**Interfaces:**
- Consumes: Task 1 rules
- Produces: smaller AAB + mapping for Task 3/4
- [ ] **Step 1: Build**
```bash
export PATH="/home/ifitworks/flutter/bin:/mnt/d/android-sdk/platform-tools:$PATH"
cd /mnt/d/Flutter_Projects/TimbanganSiab
flutter build appbundle --release --obfuscate --split-debug-info=build/debug-info --dart-define=SIAB_DEVICE_NAME=SIAB
```
Expected: `✓ Built ... app-release.aab`. Fail if build fails.
- [ ] **Step 2: Verify DEX shrank and mapping regenerated**
```bash
unzip -p build/app/outputs/bundle/release/app-release.aab base/dex/classes.dex | wc -c
# must be < 1097572 (baseline)
ls -la build/app/outputs/mapping/release/mapping.txt
# must exist, freshly created
grep -c "^io.flutter" build/app/outputs/mapping/release/mapping.txt || true
```
- [ ] **Step 3: Check build log for R8 missing-class warnings** — if any `Missing class` warnings appear, add ONLY `-dontwarn <exact.class>` lines for those classes (append to proguard-rules.pro), never `-keep`.
- [ ] **Step 4: Record actual DEX size in report; no commit needed (build outputs are gitignored).**
---
### Task 3: On-device smoke test (Redmi A5)
**Files:**
- Installs `app-release.apk` to device
**Interfaces:**
- Consumes: Task 2 build config; same flags for APK build
- Produces: pass/fail runtime evidence for Task 4
- [ ] **Step 1: Ensure device present** — if `adb devices` empty or `no permissions`: `"/mnt/c/Program Files/usbipd-win/usbipd.exe" attach --wsl --busid 1-2; sleep 5; adb kill-server; adb devices -l`. Need status `device`.
- [ ] **Step 2: Build + install**
```bash
export PATH="/home/ifitworks/flutter/bin:/mnt/d/android-sdk/platform-tools:$PATH"
cd /mnt/d/Flutter_Projects/TimbanganSiab
flutter build apk --release --obfuscate --split-debug-info=build/debug-info --dart-define=SIAB_DEVICE_NAME=SIAB
adb install -r build/app/outputs/flutter-apk/app-release.apk
```
Expected: `Success`. If `INSTALL_FAILED_UPDATE_INCOMPATIBLE` → `adb uninstall id.ptsemangat.siab_scale` then retry.
- [ ] **Step 3: Launch + crash scan**
```bash
adb logcat -c
adb shell monkey -p id.ptsemangat.siab_scale -c android.intent.category.LAUNCHER 1
sleep 12
adb shell pidof id.ptsemangat.siab_scale # must print PID (app alive)
adb logcat -d | grep -iE "FATAL|NoClassDef|NoSuchMethod|UnsatisfiedLink|ClassNotFoundException" | head -20
# expected: no matches
```
- [ ] **Step 4: Manual checks by user** (blocks Task 4): BLE scan lists devices; weighing screen loads; history screen loads (sqflite); CSV share works (share_plus). Record user confirmation in report.
---
### Task 4: Commit and hand off push
**Files:** none beyond Task 1 commit (verify clean `git status` except gitignored build/)
- [ ] **Step 1:** `git status` — confirm only build/ artifacts untracked; commit anything from Task 2 Step 3 if proguard file changed again.
- [ ] **Step 2:** Report: baseline vs new DEX sizes, mapping exists, device test result, user manual-check confirmation.
- [ ] **Step 3:** Print push command for user (does NOT run push — needs user auth):
```bash
git push origin main
```