docs: DEX optimization plan (strip proguard blanket keeps, DEX -43.9%)
This commit is contained in:
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
|
||||
```
|
||||
Reference in new issue
Block a user