Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[NativeAOT] Add support for
Application
subclasses (#9716)
Context: xamarin/monodroid@1a931e2 `android.app.Application` and `android.app.Instrumentation` are ["special"][0], in terms of app startup and type registration, because MonoVM is initialized via a `ContentProvider`, which is constructed *after* `Application` instance is constructed. In broad terms: 1. `Application` instance is created. (Specific type is via [`//application/@android:name`][1].) 2. `ContentProvider`s are created, including `MonoRuntimeProvider` (MonoVM) or `NativeAotRuntimeProvider` (NativeAOT). 3. `*RuntimeProvider.attachInfo()` invoked, provided (1). 4. `*RuntimeProvider.attachInfo()` does whatever runtime init is required, e.g. MonoVM's `MonoRuntimeProvider` calls `MonoPackageManager.LoadApplication()`. We cannot dispatch Java `native` methods into managed code until *after* (4) finishes. Meanwhile, we allow C# to subclass `Android.App.Application` and override methods like `Application.OnCreate()`: [Application (Name = "my.MainApplication")] public partial class MainApplication : Application { public override void OnCreate () { base.OnCreate (); } } How does that work? It works via a leaky abstraction: unlike other types, the Java Callable Wrapper (JCW) for `Application` and `Instrumentation` subclasses lacks: 1. A `Runtime.register()` invocation in the static constructor, and 2. A call to `TypeManager.Activate()` in the instance constructor. Compare an `Activity` JCW: public /* partial */ class MainActivity extends android.app.Activity { static { __md_methods = "…"; mono.android.Runtime.register ("….MainActivity, …", MainActivity.class, __md_methods); } public MainActivity () { super (); if (getClass () == MainActivity.class) { mono.android.TypeManager.Activate ("….MainActivity, …", "", this, new java.lang.Object[] { }); } } } to an `Application` JCW: public /* partial */ class MainApplication extends android.app.Application { { static { // mostly empty }} public MainApplication () { // Used to provide `Android.App.Application.Context` property mono.MonoPackageManager.setContext (this); // No `TypeManager.Activate(…)` call } } Instead of a `Runtime.register()` invocation in the e.g. `MainApplication` static constructor, `MainApplication` is instead registered via `ApplicationRegistration.registerApplications()`, which is: 1. Generated via the `<GenerateJavaStubs/>` task, and 2. Invoked from `MonoPackageManager.LoadApplication()`. "Later", when the Java `Application.onCreate()` method is invoked, the `Application.n_OnCreate()` marshal method will call `Java.Lang.Object.GetObject<Application>(…)`. This will look for the `(IntPtr, JniHandleOwnership)` "activation constructor", which must be present on the C# type: public partial class MainApplication : Application { public MainApplication (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) { } } To support `Application` subclasses under NativeAOT, we need to ensure that the `<GenerateJavaStubs/>` task creates `ApplicationRegistration.java`, and update `NativeAotRuntimeProvider` to invoke `ApplicationRegistration.registerApplications()`. We also need various changes to `NativeAotValueManager` so that we can create the `MainApplication` "proxy" instance via the activation constructor. Other changes: * Change the package name for `ApplicationRegistration` from `mono.android` to `net.dot.android`. * Stop using `AndroidValueManager` and instead copy [`ManagedValueManager`][2] into `NativeAotValueManager`. * Allow `CodeGenerationTarget` to be explicitly provided to `<GenerateJavaStubs/>`, instead of inferring it based on `$(_AndroidRuntime)`. [0]: https://learn.microsoft.com/en-us/previous-versions/xamarin/android/internals/architecture#java-activation [1]: https://developer.android.com/guide/topics/manifest/application-element#nm [2]: https://github.com/dotnet/java-interop/blob/dd3c1d0514addfe379f050627b3e97493e985da6/src/Java.Runtime.Environment/Java.Interop/ManagedValueManager.cs
- Loading branch information