Skip to content

Make Android ProGuard/R8 rules configurable from pyproject.toml - #6741

Open
FeodorFitsner wants to merge 1 commit into
mainfrom
feat/android-proguard-rules
Open

Make Android ProGuard/R8 rules configurable from pyproject.toml#6741
FeodorFitsner wants to merge 1 commit into
mainfrom
feat/android-proguard-rules

Conversation

@FeodorFitsner

@FeodorFitsner FeodorFitsner commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

The generated Android project's app/proguard-rules.pro is a fixed template file, so an app that needs an extra keep rule has no way to add one short of downloading the published build template, patching the file and passing --template.

Why this matters

R8 renames classes in release builds. Pyjnius's autoclass() resolves Java classes by their fully-qualified name at runtime, so autoclass() on a class bundled by a Flutter plugin — or by the app's own Java/Kotlin — fails once R8 has renamed it.

It fails hard. JNI FindClass returns null and the process aborts:

JNI DETECTED ERROR IN APPLICATION: obj == null in call to CallObjectMethodA
Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE)

That's a debuggable build, where CheckJNI supplies the message; on a production build there is usually no message at all, just a crash. It is not a catchable Python exception, so a try/except around autoclass() does not help. And because R8 only runs in release builds, it never reproduces in debug.

Android framework classes (android.os.Build and friends) live outside the APK and never needed a rule.

What's added

[tool.flet.android]
proguard_rules = ["-keep class com.example.mylib.** { *; }"]
proguard_default_rules = false

Two settings, because adding and removing rules are different problems:

  • proguard_rules appends to the defaults. R8 has no directive that undoes a keep, so a user rule can only ever widen what is kept.
  • proguard_default_rules = false drops the defaults. This is the only way to shed -keepnames class * { *; }.

They're deliberately separate. If the only option were to replace, users would paste today's defaults into their pyproject.toml and silently hold them after the defaults change.

On dropping the defaults

-keepnames class * { *; } keeps every class and member name in the app, switching off obfuscation app-wide and blocking the R8 passes that rely on renaming. Measured on Flet Studio with everything else held constant (same build/flutter tree, only Gradle re-run):

classes.dex Δ
current defaults 5,885,600
without -keepnames 3,351,712 -2,533,888 (-43.1%)

Note the AAB total barely moves, because proguard.map grows by a similar amount — but that lives under BUNDLE-METADATA/ and is never delivered to devices, so the on-device saving is the full 2.5 MB.

Dropping the defaults is safe for pyjnius's PythonActivity access: serious_python_android 4.1.0+ ships -keep class com.flet.serious_python_android.** { *; } in its own consumer-rules.pro, so it applies whether or not the template repeats it. Verified on a device (moto g15, Android 15) — with the template rule removed, PythonActivity.mActivity still resolves under 8,209 renamed classes.

Testing

Real builds against this branch's template, checking the generated proguard-rules.pro:

Config Rendered file
(nothing) byte-identical to the old static file
proguard_rules = [...] defaults + user rules
proguard_default_rules = false + rules user rules only

Also exercised the failure this unblocks: a release build with -keepnames narrowed crashes with the SIGABRT above on autoclass("io.flutter.embedding.android.FlutterActivity") (renamed to x2.d), and passes with a keep rule added through the new setting.

Defaults are unchanged, so existing builds are unaffected.

Follows existing patterns

Same shape as android.permission / android.meta_data / android.gradle_properties: defaults in setup_template_data(), combined with the pyproject value, passed through template_data["options"], rendered by the template.

Summary by Sourcery

Make Android ProGuard/R8 rules in generated projects configurable via pyproject.toml while preserving existing defaults by default.

New Features:

  • Allow configuring Android ProGuard/R8 rules from pyproject.toml using [tool.flet.android].proguard_rules and [tool.flet.android].proguard_default_rules to control default and custom keep rules.

Enhancements:

  • Update Android build template generation to render proguard-rules.pro from the configured rule set instead of a fixed static file.
  • Document the new ProGuard/R8 configuration options, their impact on R8 obfuscation, and example usages in the Android publishing guide.
  • Note the new ProGuard/R8 configurability in the changelog for the release.

Documentation:

  • Add user-facing documentation explaining how to configure ProGuard/R8 rules from pyproject.toml, including when Pyjnius-based apps need keep rules and the effect of dropping default rules.

The generated Android project's app/proguard-rules.pro was a fixed template
file, so an app that needed an extra keep rule had no way to add one short of
downloading the published build template, patching the file and passing
--template.

This matters for pyjnius. `autoclass()` resolves Java classes by their
fully-qualified name at runtime, and R8 renames anything in the APK that is not
kept, so `autoclass()` on a class bundled by a Flutter plugin or by the app's
own Java/Kotlin fails in release builds. It fails hard: JNI FindClass returns
null and the process aborts -- "JNI DETECTED ERROR IN APPLICATION: obj == null"
followed by SIGABRT on a debuggable build, and usually no message at all on a
production one -- rather than raising a catchable Python exception. Because R8
only runs in release builds it never reproduces in debug. Android framework
classes (android.os.Build and friends) live outside the APK and never needed a
rule.

Adds two settings, because adding and removing rules are different problems:

  [tool.flet.android]
  proguard_rules = ["-keep class com.example.mylib.** { *; }"]
  proguard_default_rules = false

`proguard_rules` appends to the defaults, since R8 has no directive that undoes
a keep -- a user rule can only ever widen what is kept. Removing a default
therefore needs its own switch, and `proguard_default_rules = false` is the
only way to shed `-keepnames class * { *; }`. That rule keeps every class and
member name in the app, switching off obfuscation app-wide and blocking the R8
passes that rely on renaming; dropping it takes Flet Studio's classes.dex from
5,885,600 to 3,351,712 bytes (-43%), measured with everything else held
constant.

Dropping the defaults is safe for pyjnius's PythonActivity access:
serious_python_android 4.1.0+ ships the same keep rule in its own
consumer-rules.pro, so it applies whether or not the template repeats it.
Verified on device -- with the template rule removed, PythonActivity.mActivity
still resolves under 8,209 renamed classes.

The two settings are kept separate on purpose. If the only option were to
replace, users would paste today's defaults into their pyproject.toml and
silently keep them after the defaults change.

Follows the existing pattern for android.permission / android.meta_data /
android.gradle_properties: defaults in setup_template_data(), combined with the
pyproject value, passed through template_data["options"], rendered by the
template. With no settings the template renders byte-identically to the old
static file, so existing builds are unaffected.

@sourcery-ai sourcery-ai 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.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying flet-website-v2 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 25b5933
Status: ✅  Deploy successful!
Preview URL: https://6c3b1866.flet-website-v2.pages.dev
Branch Preview URL: https://feat-android-proguard-rules.flet-website-v2.pages.dev

View logs

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.

1 participant