Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Commit 5852e6e

Browse files
authored
[Java.Interop] Use Class.forName() as fallback to load Java classes (#1326)
Fixes: #23 Context: dotnet/android@aba2726 Context: b4d44e4 Context: https://github.com/xamarin/monodroid/commit/ed984a3a0bfbe71f6499a7855c647b5bc2b28466 Context: dotnet/android#7616 Commit b4d44e4 noted: > Android is..."special", in that not all threads get the same > ClassLoader behavior. Specifically, *managed* threads -- > System.Threading.Thread instances -- get a different ClassLoader than > the main/UI thread on Android. (Untested, but the ClassLoader *may* > behave sanely if you use a java.lang.Thread instance instead. But who > wants to use java.lang.Thread instances...?) dotnet/android#7616 provided additional context: `JNIEnv::FindClass()` behavior *is* tied to the thread that calls it, and one of the knock-on effects is that `Java.Lang.JavaSystem.LoadLibrary("MyLib")` doesn't work properly when invoked from a new `System.Threading.Thread` thread. (This is still the case, by the way.) Which brings us to xamarin/mondroid@ed984a3a, which updated then Xamarin.Android to use [`Class.forName(String name, boolean initialize, ClassLoader loader)`][0] instead of [`ClassLoader.loadClass(String)`][1], because the "real" JDK cannot use `ClassLoader.loadClass(String)` to load array types: Class c1 = Class.forName("[Ljava.lang.String;"); // works Class c2 = ClassLoader.getSystemClassLoader() .loadClass("[Ljava.lang.String;"); // throws java.lang.ClassNotFoundException Class c3 = Class.forName("[I"); // works; array of int Class c4 = ClassLoader.getSystemClassLoader() .loadClass("[I"); // throws java.lang.ClassNotFoundException Using `ClassLoader.loadClass(String)` to load array types works on Android, presumably as an undocumented implementation detail, but as xamarin/monodroid@ed984a3a was trying to get things working within the (now dead) Android Designer -- which ran using a Desktop JDK -- Android-specific extensions were not available. Update `JniEnvironment.Types` to use `Class.forName(String)` instead of `ClassLoader.loadClass(String)` to load Java classes, as a fallback for when `JNIEnv::FindClass()` fails to find the class. `[Obsolete]` the `JniRuntime.CreationOptions.ClassLoader_LoadClass_id` property as it is no longer used. [0]: https://developer.android.com/reference/java/lang/Class#forName(java.lang.String,%20boolean,%20java.lang.ClassLoader) [1]: https://developer.android.com/reference/java/lang/ClassLoader#loadClass(java.lang.String)
1 parent bc44f08 commit 5852e6e

2 files changed

Lines changed: 18 additions & 23 deletions

File tree

‎src/Java.Interop/Java.Interop/JniEnvironment.Types.cs‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ static partial class Types {
2424
};
2525

2626
staticreadonlyJniMethodInfoClass_getName;
27+
staticreadonlyJniMethodInfoClass_forName;
28+
staticreadonlyJniObjectReferenceClass_reference;
2729

2830
staticTypes()
2931
{
3032
using(vart=newJniType("java/lang/Class")){
33+
Class_reference=t.PeerReference.NewGlobalRef();
3134
Class_getName=t.GetInstanceMethod("getName","()Ljava/lang/String;");
35+
Class_forName=t.GetStaticMethod("forName","(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
3236
}
3337
}
3438

@@ -57,12 +61,14 @@ static unsafe JniObjectReference TryFindClass (string classname, bool throwOnErr
5761
LogCreateLocalRef(findClassThrown);
5862
varpendingException=info.Runtime.GetExceptionForThrowable(reffindClassThrown,JniObjectReferenceOptions.CopyAndDispose);
5963

60-
if(info.Runtime.ClassLoader_LoadClass!=null){
64+
if(Class_forName.IsValid){
6165
varjava=info.ToJavaName(classname);
62-
var__args=stackallocJniArgumentValue[1];
66+
var__args=stackallocJniArgumentValue[3];
6367
__args[0]=newJniArgumentValue(java);
68+
__args[1]=newJniArgumentValue(true);// initialize the class
69+
__args[2]=newJniArgumentValue(info.Runtime.ClassLoader);
6470

65-
c=RawCallObjectMethodA(info.EnvironmentPointer,outthrown,info.Runtime.ClassLoader.Handle,info.Runtime.ClassLoader_LoadClass.ID,(IntPtr)__args);
71+
c=RawCallStaticObjectMethodA(info.EnvironmentPointer,outthrown,Class_reference.Handle,Class_forName.ID,(IntPtr)__args);
6672
JniObjectReference.Dispose(refjava);
6773
if(thrown==IntPtr.Zero){
6874
(pendingExceptionasIJavaPeerable)?.Dispose();
@@ -169,12 +175,12 @@ static void RawExceptionClear (IntPtr env)
169175
#endif // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
170176
}
171177

172-
staticIntPtrRawCallObjectMethodA(IntPtrenv,outIntPtrthrown,IntPtrinstance,IntPtrjmethodID,IntPtrargs)
178+
staticIntPtrRawCallStaticObjectMethodA(IntPtrenv,outIntPtrthrown,IntPtrclazz,IntPtrjmethodID,IntPtrargs)
173179
{
174180
#if FEATURE_JNIENVIRONMENT_JI_PINVOKES
175-
returnNativeMethods.java_interop_jnienv_call_object_method_a(env,outthrown,instance,jmethodID,args);
181+
returnNativeMethods.java_interop_jnienv_call_static_object_method_a(env,outthrown,clazz,instance,jmethodID,args);
176182
#elif FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS
177-
varr=JniNativeMethods.CallObjectMethodA(env,instance,jmethodID,args);
183+
varr=JniNativeMethods.CallStaticObjectMethodA(env,clazz,jmethodID,args);
178184
thrown=JniNativeMethods.ExceptionOccurred(env);
179185
returnr;
180186
#else // FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS

‎src/Java.Interop/Java.Interop/JniRuntime.cs‎

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public partial class CreationOptions {
5959
publicIntPtrEnvironmentPointer{get;set;}
6060

6161
publicJniObjectReferenceClassLoader{get;set;}
62+
[Obsolete("No longer supported; Class.forName() is now used instead")]
6263
publicIntPtrClassLoader_LoadClass_id{get;set;}
6364

6465
publicJniObjectReferenceManager?ObjectReferenceManager{get;set;}
@@ -152,7 +153,6 @@ public static void SetCurrent (JniRuntime newCurrent)
152153
boolDestroyRuntimeOnDispose;
153154

154155
internalJniObjectReferenceClassLoader;
155-
internalJniMethodInfo?ClassLoader_LoadClass;
156156

157157
publicIntPtrInvocationPointer{get;privateset;}
158158

@@ -206,25 +206,14 @@ protected JniRuntime (CreationOptions options)
206206
JniEnvironment.SetEnvironmentInfo(env);
207207

208208
ClassLoader=options.ClassLoader;
209-
if(options.ClassLoader_LoadClass_id!=IntPtr.Zero){
210-
ClassLoader_LoadClass=newJniMethodInfo(options.ClassLoader_LoadClass_id,isStatic:false);
211-
}
212-
213209
if(ClassLoader.IsValid){
214210
ClassLoader=ClassLoader.NewGlobalRef();
215-
}
216-
217-
if(!ClassLoader.IsValid||ClassLoader_LoadClass==null){
211+
}else{
218212
using(vart=newJniType("java/lang/ClassLoader")){
219-
if(!ClassLoader.IsValid){
220-
varm=t.GetStaticMethod("getSystemClassLoader","()Ljava/lang/ClassLoader;");
221-
varloader=JniEnvironment.StaticMethods.CallStaticObjectMethod(t.PeerReference,m);
222-
ClassLoader=loader.NewGlobalRef();
223-
JniObjectReference.Dispose(refloader);
224-
}
225-
if(ClassLoader_LoadClass==null){
226-
ClassLoader_LoadClass=t.GetInstanceMethod("loadClass","(Ljava/lang/String;)Ljava/lang/Class;");
227-
}
213+
varm=t.GetStaticMethod("getSystemClassLoader","()Ljava/lang/ClassLoader;");
214+
varloader=JniEnvironment.StaticMethods.CallStaticObjectMethod(t.PeerReference,m);
215+
ClassLoader=loader.NewGlobalRef();
216+
JniObjectReference.Dispose(refloader);
228217
}
229218
}
230219

0 commit comments

Comments
 (0)