Skip to content

Conversation

@jonpryor
Copy link
Contributor

@jonpryor jonpryor commented May 1, 2024

Context: 2197579

Commit 2197579 added samples/Hello-NativeAOTFromJNI, which
demonstrated the use of NativeAOT to create a native library which
could be loaded and used by a Java application.

What else supports loading native libraries for use by a "Java"
environment? Android!

Take the core infrastructure from Hello-NativeAOTFromJNI, have
the binary output target linux-bionic-arm64 --
the $(RuntimeIdentifier) for .NET using Android's "bionic" libc
while not using .NET for Android -- and then use gradlew to
package that native library into an Android application.

Add "degrees" of Android and Gradle Integration:

  • samples/Hello-NativeAOTFromAndroid/app is an Android project
    with Gradle build scripts.

  • gradlew :app:processReleaseResources is executed as a pre-build
    step to get an R.txt describing all the Android Resources
    contained within the Android project. The new
    <ParseAndroidResources/> task parses R.txt and generates an
    R.g.cs, allowing C# code to reference some Android resources,
    such as the Android layout to display.

  • android.xml contains a (very!) minimal API description of
    android.jar. It is used in a pre-build step to produce bindings
    of android.app.Activity and android.os.Bundle (among others),
    allowing us to write a C# subclass of Activity and show
    something on-screen.

  • After the C# build, we:

    • use jcw-gen to generate Java Callable Wrappers (JCW) of
      Java.Lang.Object subclasses,
    • use jnimarshalmethod-gen to generate JNI marshal methods
      for methods such as MainActivity.OnCreate(), so that
      C# code can override Java methods.
  • As a post-Publish step, we copy various artifacts into the
    Android project structure so that they can be packaged into the
    Android app-release.apk, then invoke gradlew assembleRelease
    to generate app-release.apk.

Update generator to support using Java.Base.dll as a referenced
assembly for binding purposes, in particular by supporting the use
of [JniTypeSignatureAttribute] on already bound types. No usable
bindings of types in android.xml could be generated without this.

Update jcw-gen to appropriately support
[JniTypeSignature(GenerateJavaPeer=false)] when determining the
constructors that a Java Callable Wrapper should contain. Failure to
do so meant that the JCW for MainActivity contained constructors
that shouldn't be there, resulting in javac errors.

Update jcw-gen to and support JavaInterop1-style method overrides.
Failure to do so meant that the JCW for MainActivity didn't
declare an onCreate() method.

@jonpryor jonpryor force-pushed the dev/jonp/jonp-nativeaot+android branch 6 times, most recently from b23d06e to 886852f Compare May 3, 2024 16:56
@jonpryor jonpryor marked this pull request as ready for review May 3, 2024 17:46
@jonpryor jonpryor requested a review from jpobst May 3, 2024 17:46
@jonpryor jonpryor force-pushed the dev/jonp/jonp-nativeaot+android branch 2 times, most recently from 27c79dc to fe45822 Compare May 3, 2024 19:36
Context: 2197579

Commit 2197579 added `samples/Hello-NativeAOTFromJNI`, which
demonstrated the use of NativeAOT to create a native library which
could be loaded and used by a Java application.

What else supports loading native libraries for use by a "Java"
environment?  Android!

Take the core infrastructure from `Hello-NativeAOTFromJNI`, have
the binary output target `linux-bionic-arm64` --
the `$(RuntimeIdentifier)` for .NET using Android's "bionic" libc
while *not* using .NET for Android -- and then use `gradlew` to
package that native library into an Android application.

Add "degrees" of Android and Gradle Integration:

  * `samples/Hello-NativeAOTFromAndroid/app` is an Android project
    with Gradle build scripts.

  * `gradlew :app:processReleaseResources` is executed as a pre-build
    step to get an `R.txt` describing all the Android Resources
    contained within the Android project.  The new
    `<ParseAndroidResources/>` task parses `R.txt` and generates an
    `R.g.cs`, allowing C# code to reference some Android resources,
    such as the Android layout to display.

  * `android.xml` contains a (very!) minimal API description of
    `android.jar`.  It is used in a pre-build step to produce bindings
    of `android.app.Activity` and `android.os.Bundle` (among others),
    allowing us to write a C# subclass of `Activity` and show
    something on-screen.

  * After the C# build, we:
      * use `jcw-gen` to generate Java Callable Wrappers (JCW) of
        `Java.Lang.Object` subclasses,
      * use `jnimarshalmethod-gen` to generate JNI marshal methods
        for methods such as `MainActivity.OnCreate()`, so that
        C# code can override Java methods.

  * As a post-`Publish` step, we copy various artifacts into the
    Android project structure so that they can be packaged into the
    Android `app-release.apk`, then invoke `gradlew assembleRelease`
    to generate `app-release.apk`.

Update `generator` to support using `Java.Base.dll` as a referenced
assembly for binding purposes, in particular by supporting the use
of `[JniTypeSignatureAttribute]` on already bound types.  No usable
bindings of types in `android.xml` could be generated without this.

Update `jcw-gen` to appropriately support
`[JniTypeSignature(GenerateJavaPeer=false)]` when determining the
constructors that a Java Callable Wrapper should contain.  Failure to
do so meant that the JCW for `MainActivity` contained constructors
that shouldn't be there, resulting in `javac` errors.

Update `jcw-gen` to and support `JavaInterop1`-style method overrides.
Failure to do so meant that the JCW for `MainActivity` *didn't*
declare an `onCreate()` method.
@jonpryor jonpryor force-pushed the dev/jonp/jonp-nativeaot+android branch from fe45822 to f937715 Compare May 6, 2024 17:12
Copy link
Member

@jonathanpeppers jonathanpeppers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, nothing seems too concerning. 👍

[UnmanagedCallersOnly (EntryPoint="Java_net_dot_jni_nativeaot_JavaInteropRuntime_init")]
static void init (IntPtr jnienv, IntPtr klass)
{
Console.WriteLine ($"C# init()");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you wanted to remove:

Suggested change
Console.WriteLine ($"C# init()");
Console.WriteLine ("C# init()");

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The C# compiler is able to optimize the former into the latter, meaning there isn't a reason to not use $"…" strings everywhere. This has the added benefit that you don't need to remember to add $ to your strings when you do want to use formatted strings, which is something I occasionally forget…

method = bmethod;

if (method.AnyCustomAttributes (typeof (RegisterAttribute))) {
if (GetMethodRegistrationAttributes (method).Any ()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this called by xamarin-android at all?

Just want to make sure we wouldn't make this too much worse:

I guess this could create an attribute instance, an IEnumerable, and then call .Any().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this codepath is used by .NET for Android, in which case the problem with this change is that it could introduce additional object allocations that aren't needed. I'll look into updating this.

Don't ignore the IL2026 warning; it isn't even emitted!

"Optimize" `CecilExtensions.GetBaseRegisteredMethod()` to avoid
unnecessary object allocations by adding a new
`CecilExtensions.HasMethodRegistrationAttributes()` method.
@jonpryor jonpryor merged commit 78d5937 into main May 8, 2024
@jonpryor jonpryor deleted the dev/jonp/jonp-nativeaot+android branch May 8, 2024 19:13
jonpryor pushed a commit to dotnet/android that referenced this pull request May 9, 2024
Changes: dotnet/java-interop@4e893bf...78d5937

  * dotnet/java-interop@78d59371: [Hello-NativeAOTFromAndroid] Add NativeAOT+Android sample (dotnet/java-interop#1218)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@github-actions github-actions bot locked and limited conversation to collaborators Jun 9, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants