NkkinSoft: practical notes on building, publishing and monetizing Android apps with .NET for Android, Google Play policies, AdMob and app development tools.

Google Play publishing & policy

.NET for Android

Post Page Advertisement [Top]

Writing R8 rules for .NET for Android: what the SDK generates and what you add

Writing R8 rules for .NET for Android: what the SDK generates and what you add


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, TrimmerRootAssembly and a linker.xml root 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, -dontobfuscate unless 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 keep PublishTrimmed=true in 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 -dontwarn lines 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.
  • -dontobfuscate in 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

Bottom Ad [Post Page]