Skip to content

[Mono.Android] update JNIEnv.FindClass() to use JniEnvironment.Types.FindClass() - #9769

Merged
jonpryor merged 1 commit into
mainfrom
dev/peppers/JNIEnv.FindClass
Feb 8, 2025
Merged

[Mono.Android] update JNIEnv.FindClass() to use JniEnvironment.Types.FindClass()#9769
jonpryor merged 1 commit into
mainfrom
dev/peppers/JNIEnv.FindClass

Conversation

@jonathanpeppers

Copy link
Copy Markdown
Member

This avoids the problem of JNIEnvInit.mid_Class_forName being null in a NativeAOT context:

02-07 14:05:25.954 30744 30744 E AndroidRuntime: Process: net.dot.hellonativeaot, PID: 30744
02-07 14:05:25.954 30744 30744 E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.TypeInitializationException: TypeInitialization_Type_NoTypeAvailable
02-07 14:05:25.954 30744 30744 E AndroidRuntime: ---> System.ArgumentNullException: ArgumentNull_Generic Arg_ParamName_Name, method
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference, JniMethodInfo, JniArgumentValue*) + 0x1c8
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Android.Runtime.JNIEnv.FindClass(String) + 0xb4
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Java.Lang.Class..cctor() + 0x7c
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0xb4
02-07 14:05:25.954 30744 30744 E AndroidRuntime: Exception_EndOfInnerExceptionStack
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0x160
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at System.Runtime.CompilerServices.ClassConstructorRunner.CheckStaticClassConstructionReturnNonGCStaticBase(StaticClassConstructionContext*, IntPtr) + 0x14
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Android.Runtime.JNIEnv.FindClass(String) + 0xc0
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Android.App.Application.get_Context() + 0x50
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.Hosting.EssentialsExtensions.<>c.<UseEssentials>b__0_0(ILifecycleBuilder life) + 0x18
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.LifecycleEvents.LifecycleEventService..ctor(IEnumerable`1) + 0x94
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.LifecycleEvents.MauiAppHostBuilderExtensions.<>c.<ConfigureLifecycleEvents>b__0_0(IServiceProvider sp) + 0x3c
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite, RuntimeResolverContext) + 0x68
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier) + 0x180
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey, Func`2) + 0x11c
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier, ServiceProviderEngineScope) + 0x44
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type) + 0x48
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type) + 0x48
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider) + 0x3c
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.LifecycleEvents.LifecycleEventServiceExtensions.<GetLifecycleEventDelegates>d__3`1.MoveNext() + 0x38
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.LifecycleEvents.LifecycleEventServiceExtensions.InvokeLifecycleEvents[TDelegate](IServiceProvider, Action`1) + 0x68
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.MauiApplication.OnCreate() + 0xb0
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Android.App.Application.n_OnCreate(IntPtr jnienv, IntPtr native__this) + 0xb0
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at my.MainApplication.n_onCreate(Native Method)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at my.MainApplication.onCreate(MainApplication.java:24)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1386)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7398)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2379)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:107)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:232)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.os.Looper.loop(Looper.java:317)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8592)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)

This also allows us to remove these that are now unused:

  • JnienvInitializeArgs.Class_forName
  • JNIEnv.BinaryName()

…es.FindClass()`
This avoids the problem of `JNIEnvInit.mid_Class_forName` being `null`
in a NativeAOT context:
02-07 14:05:25.954 30744 30744 E AndroidRuntime: Process: net.dot.hellonativeaot, PID: 30744
02-07 14:05:25.954 30744 30744 E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.TypeInitializationException: TypeInitialization_Type_NoTypeAvailable
02-07 14:05:25.954 30744 30744 E AndroidRuntime: ---> System.ArgumentNullException: ArgumentNull_Generic Arg_ParamName_Name, method
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference, JniMethodInfo, JniArgumentValue*) + 0x1c8
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Android.Runtime.JNIEnv.FindClass(String) + 0xb4
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Java.Lang.Class..cctor() + 0x7c
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0xb4
02-07 14:05:25.954 30744 30744 E AndroidRuntime: Exception_EndOfInnerExceptionStack
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0x160
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at System.Runtime.CompilerServices.ClassConstructorRunner.CheckStaticClassConstructionReturnNonGCStaticBase(StaticClassConstructionContext*, IntPtr) + 0x14
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Android.Runtime.JNIEnv.FindClass(String) + 0xc0
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Android.App.Application.get_Context() + 0x50
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.Hosting.EssentialsExtensions.<>c.<UseEssentials>b__0_0(ILifecycleBuilder life) + 0x18
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.LifecycleEvents.LifecycleEventService..ctor(IEnumerable`1) + 0x94
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.LifecycleEvents.MauiAppHostBuilderExtensions.<>c.<ConfigureLifecycleEvents>b__0_0(IServiceProvider sp) + 0x3c
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite, RuntimeResolverContext) + 0x68
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier) + 0x180
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey, Func`2) + 0x11c
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier, ServiceProviderEngineScope) + 0x44
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type) + 0x48
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type) + 0x48
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider) + 0x3c
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.LifecycleEvents.LifecycleEventServiceExtensions.<GetLifecycleEventDelegates>d__3`1.MoveNext() + 0x38
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.LifecycleEvents.LifecycleEventServiceExtensions.InvokeLifecycleEvents[TDelegate](IServiceProvider, Action`1) + 0x68
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Microsoft.Maui.MauiApplication.OnCreate() + 0xb0
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at Android.App.Application.n_OnCreate(IntPtr jnienv, IntPtr native__this) + 0xb0
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at my.MainApplication.n_onCreate(Native Method)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at my.MainApplication.onCreate(MainApplication.java:24)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1386)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7398)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2379)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:107)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:232)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.os.Looper.loop(Looper.java:317)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8592)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)
This also allows us to remove these that are now unused:
* `JnienvInitializeArgs.Class_forName`
* `JNIEnv.BinaryName()`
@jonathanpeppers

Copy link
Copy Markdown
MemberAuthor

@jonathanpeppers
jonathanpeppers marked this pull request as ready for review February 7, 2025 23:06
@jonpryor

Copy link
Copy Markdown
Contributor

@jonathanpeppersasked:

Which isn't done yet in Java.Interop, is that important?

Good question! I forget.

This is the commit which migrated Mono.Android.dll to using Class.forName():

https://github.com/xamarin/monodroid/commit/ed984a3a0bfbe71f6499a7855c647b5bc2b28466

[Mono.Android] Switch JNIEnv.FindClass to use Class.forName

Previously, JNIEnv.FindClass(string) would defer to ClassLoader.loadClass to load a Java type.

Because loading some classes on Java is a bit of an arcane art, you have to use a special syntax for
primitive types. This syntax seemed to have been supported just fine on (Dalvik|ART) when using the
loadClass method but on a desktop java like the Oracle VM this is not the case anymore.

Instead, passing by the Class.forName static method seems to handle such case that involves primitive
types so this commit supply the necessary handles to JNIEnv from native and switch the FindClass method
to use them.

For instance, a type that used to trip the runtime beforehand with a ClassNotFoundException but doesn't
after this commit is '[I' (an array of primitive Java integer).

Note: this commit only fix the XA part, Java.Interop implements the same sort of type lookup and needs to
be ultimately updated to the same technique at a later stage.

This appears to have been for the Xamarin.Android Designer atop desktop Java, which is something we no longer support.

We may want to migrate to Class.forName()anyway, but this doesn't appear to be important right now.

@jonpryor

Copy link
Copy Markdown
Contributor

Draft commit message:

Context: https://github.com/xamarin/monodroid/commit/ed984a3a0bfbe71f6499a7855c647b5bc2b28466
Context: https://github.com/dotnet/java-interop/issues/23
Update `JNIEnv.FindClass()` to use `JniEnvironment.Types.FindClass()`.
This avoids the problem of `JNIEnvInit.mid_Class_forName` being
`null` in a NativeAOT context:
E AndroidRuntime: Process: net.dot.hellonativeaot, PID: 30744E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.TypeInitializationException: TypeInitialization_Type_NoTypeAvailableE AndroidRuntime: ---> System.ArgumentNullException: ArgumentNull_Generic Arg_ParamName_Name, methodE AndroidRuntime: at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference, JniMethodInfo, JniArgumentValue*) + 0x1c8E AndroidRuntime: at Android.Runtime.JNIEnv.FindClass(String) + 0xb4E AndroidRuntime: at Java.Lang.Class..cctor() + 0x7cE AndroidRuntime: at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0xb4E AndroidRuntime: Exception_EndOfInnerExceptionStackE AndroidRuntime: at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0x160E AndroidRuntime: at System.Runtime.CompilerServices.ClassConstructorRunner.CheckStaticClassConstructionReturnNonGCStaticBase(StaticClassConstructionContext*, IntPtr) + 0x14E AndroidRuntime: at Android.Runtime.JNIEnv.FindClass(String) + 0xc0E AndroidRuntime: at Android.App.Application.get_Context() + 0x50E AndroidRuntime: at Microsoft.Maui.Hosting.EssentialsExtensions.<>c.<UseEssentials>b__0_0(ILifecycleBuilder life) + 0x18E AndroidRuntime: at Microsoft.Maui.LifecycleEvents.LifecycleEventService..ctor(IEnumerable`1) + 0x94E AndroidRuntime: at Microsoft.Maui.LifecycleEvents.MauiAppHostBuilderExtensions.<>c.<ConfigureLifecycleEvents>b__0_0(IServiceProvider sp) + 0x3cE AndroidRuntime: at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite, RuntimeResolverContext) + 0x68E AndroidRuntime: at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier) + 0x180E AndroidRuntime: at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey, Func`2) + 0x11cE AndroidRuntime: at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier, ServiceProviderEngineScope) + 0x44E AndroidRuntime: at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type) + 0x48E AndroidRuntime: at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type) + 0x48E AndroidRuntime: at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider) + 0x3cE AndroidRuntime: at Microsoft.Maui.LifecycleEvents.LifecycleEventServiceExtensions.<GetLifecycleEventDelegates>d__3`1.MoveNext() + 0x38E AndroidRuntime: at Microsoft.Maui.LifecycleEvents.LifecycleEventServiceExtensions.InvokeLifecycleEvents[TDelegate](IServiceProvider, Action`1) + 0x68E AndroidRuntime: at Microsoft.Maui.MauiApplication.OnCreate() + 0xb0E AndroidRuntime: at Android.App.Application.n_OnCreate(IntPtr jnienv, IntPtr native__this) + 0xb0E AndroidRuntime: at my.MainApplication.n_onCreate(Native Method)E AndroidRuntime: at my.MainApplication.onCreate(MainApplication.java:24)E AndroidRuntime: at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1386)E AndroidRuntime: at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7398)E AndroidRuntime: at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2379)E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:107)E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:232)E AndroidRuntime: at android.os.Looper.loop(Looper.java:317)E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8592)E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)
This also allows us to remove these fields that are now unused:
*`JnienvInitializeArgs.Class_forName`*`JNIEnv.BinaryName()`
TODO? While `JNIEnv.FindClass()` was using [`Class.forName()`][0],
`JniEnvironment.Types.FindClass()` uses [`ClassLoader.loadClass()`][1].
`JNIEnv.FindClass()` originally used `ClassLoader.loadClass()`, but
was changed to use `Class.forName()` in xamarin/monodroid@ed984a3a as
part of supporting the Xamarin.Android Designer.
Updating `JNIEnv.FindClass()` to use `JniEnvironment.Types.FindClass()`
effectively reverts that original change. This is "fine" for now --
the Xamarin.Android Designer is no longer supported -- but in the
future we may want to update `JniEnvironment.Types.FindClass()` to
use `Class.forName()`; see dotnet/java-interop#23.
[0]: https://developer.android.com/reference/java/lang/Class#forName(java.lang.String)[1]: https://developer.android.com/reference/java/lang/ClassLoader#loadClass(java.lang.String)

@jonpryor
jonpryor merged commit aba2726 into mainFeb 8, 2025
@jonpryor
jonpryor deleted the dev/peppers/JNIEnv.FindClass branch February 8, 2025 18:16
@filipnavara

Copy link
Copy Markdown
Member

We may want to migrate to Class.forName()anyway, but this doesn't appear to be important right now.

The observable difference is that Class.forName runs static constructors. That could possibly be quite important I guess.

grendello added a commit that referenced this pull request Feb 10, 2025
* main:
[Mono.Android] `JNIEnv.FindClass()` uses `JniEnvironment.Types.FindClass()` (#9769)
[Mono.Android] `JavaList.Add` should allow duplicates. (#9751)
Bump to dotnet/java-interop@6bc87e8b (#9761)
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 12, 2025
Sign up for freeto 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.

3 participants

@jonathanpeppers@jonpryor@filipnavara