Skip to content

Commit

Permalink
[Mono.Android] Obsolete [Preserve] (#6129)
Browse files Browse the repository at this point in the history
Fixes: #5664

The .NET 5+ replacement for [`Android.Runtime.PreserveAttribute`][0]
is [`System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute`][1].

One "discrepancy" is that the relationship is "backwards":
`[Preserve]` is an *assertion* that the member is required, and that
the member should be preserved by the linker.

`DynamicDependencyAttribute` is an indicator: *if* the member with
`[DynamicDependency]` is preserved, then the members referenced by
`[DynamicDependency]` will be preserved.

Consider commit 15269f6: in a `[Preserve]` world, we had:

	// `Mono.Android.dll`
	[Preserve(AllMembers=true)] partial class JavaCollection {
	    public static IntPtr ToLocalJniHandle (ICollection? items) {…}
	}
	[Preserve(AllMembers=true)] partial class JavaDictionary {
	    public static IntPtr ToLocalJniHandle (ICollection? items) {…}
	}
	// …

	// `Mono.Android.Export.dll`
	partial class DynamicInvokeTypeInfo {
	    public CodeExpression ToNative (CodeExpression arg) {
	        switch (GetKind (type)) {
	            case SymbolKind.Collection: return new CodeMethodCall (type.GetMethod ("ToLocalJniHandle"), arg);
	        }
	    }
	}

This setup meant that even when `Mono.Android.Export.dll` *wasn't*
used, all members of `JavaCollection` and `JavaDictionary` were
preserved by the linker, increasing `.apk` size.

With a `[DynamicDependency]` world in commit 15269f6, we now have:

	// `Mono.Android.dll`
	partial class JavaCollection {
	    public static IntPtr ToLocalJniHandle (ICollection? items) {…}
	}
	partial class JavaDictionary {
	    public static IntPtr ToLocalJniHandle (ICollection? items) {…}
	}
	// …

	// `Mono.Android.Export.dll`
	partial class DynamicInvokeTypeInfo {
	    [DynamicDependency ("ToLocalJniHandle", "Android.Runtime.JavaCollection", "Mono.Android")]
	    [DynamicDependency ("ToLocalJniHandle", "Android.Runtime.JavaDictionary", "Mono.Android")]
	    public CodeExpression ToNative (CodeExpression arg) {
	        switch (GetKind (type)) {
	            case SymbolKind.Collection: return new CodeMethodCall (type.GetMethod ("ToLocalJniHandle"), arg);
	        }
	    }
	}

Now, if `Mono.Android.Export.dll` *isn't* used, then `JavaCollection`
and `JavaDictionary` can be removed, enabling smaller apps:

	> apkdiff -f -e dll$ before.apk after.apk
	Size difference in bytes ([*1] apk1 only, [*2] apk2 only):
	  -         477 assemblies/Java.Interop.dll
	  -         603 assemblies/System.Collections.Concurrent.dll
	  -       1,388 assemblies/System.Private.CoreLib.dll
	  -      30,291 assemblies/Mono.Android.dll
	Summary:
	  -      32,759 Assemblies -4.19% (of 781,837)
	  -      32,768 Package size difference -0.43% (of 7,645,409)

[0]: https://docs.microsoft.com/en-us/dotnet/api/foundation.preserveattribute?view=xamarin-ios-sdk-12
[1]: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.dynamicdependencyattribute?view=net-5.0
  • Loading branch information
jonpryor authored Jul 27, 2021
1 parent dcb9294 commit e604833
Showing 1 changed file with 3 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/Mono.Android/Android.Runtime/PreserveAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@

namespace Android.Runtime {

#if NETCOREAPP
[Obsolete ("Please use [System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute]")]
#endif // NETCOREAPP
[AttributeUsage (
AttributeTargets.Class
| AttributeTargets.Struct
Expand Down

0 comments on commit e604833

Please sign in to comment.