If you build Android apps with .NET (formerly Xamarin.Android), you probably have a proguard.cfg that grew over the years by copy and paste. Ours had rules for LiteDB, System.Text.Json and our C# models. None of them did anything. This post explains why, and what a clean configuration looks like.
Two different trimmers
A .NET Android release build has two separate shrinking steps:
- ILLink (the .NET trimmer) removes unused C# code. You control it with
TrimMode,TrimmerRootAssemblyand alinker.xmlroot descriptor. - R8 shrinks the Java code in the DEX: Android entry points, Java Callable Wrappers for your C# Activities and Views, and bound libraries such as AndroidX, Material, Glide and Google Play services.
Plain C# code never becomes a DEX class. A rule like -keep class LiteDB.** { *; } in proguard.cfg matches nothing. If LiteDB needs protecting from trimming (it does, because it maps documents through reflection), that belongs in linker.xml:
<linker>
<assembly fullname="LiteDB">
<namespace fullname="LiteDB" preserve="all" />
</assembly>
</linker>
What the SDK already does for you
With R8 enabled, the build generates its own rule files under obj/:
proguard_xamarin.cfg: runtime bridge rules (and, on .NET 10,-dontobfuscateunless you opt in to obfuscation).proguard_project_primary.cfg: a keep rule for every Java Callable Wrapper, meaning your C# Activities, Services and custom Views.proguard_project_references.cfg: a keep rule for every Java type your remaining C# code binds to. It is generated only when trimming is on, so keepPublishTrimmed=truein Release.
Libraries also ship their own "consumer" rules inside their AAR files. AndroidX, Material and Google Play services all do.
What's left for your file
Your own proguard.cfg should be short and only cover what nothing else knows about:
- Classes created by name from XML or reflection that aren't Java Callable Wrappers (custom Views get their standard constructors kept).
- Library-specific needs that aren't covered by consumer rules. For Glide, use Glide's official rules rather than
-keep class com.bumptech.glide.** { *; }, which stops R8 from shrinking the whole library. - Readable crash reports:
-keepattributes SourceFile,LineNumberTable. - Targeted
-dontwarnlines for known-harmless missing classes, instead of turning off warnings globally.
Common mistakes we removed
- Rules naming C# namespaces: no-ops.
- Old Xamarin-era rules for OpenTK and hashed package names that no longer exist: dead weight.
-dontobfuscatein your own file: it cancels obfuscation for the whole build.- Very broad
{ *; }keeps on whole libraries: they defeat shrinking.
After changing rules, always test a Release build on a real phone, covering startup, every main screen, ads and background and resume. R8 problems only show up at runtime.

No comments:
Post a Comment