You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Java.Interop] JNIEnv::FindClass() fallback is ClassLoader.loadClass()
Context: xamarin/monodroid@ea292a5
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...?)
System-provided types can be found, but *Application*-provided types
*can't* be found through this "different" ClassLoader; consequently,
JNIEnv::FindClass() throws an exception similar to [0, 1].
The Xamarin.Android solution -- ~copied here -- is to grab the
java.lang.ClassLoader instance that the main/UI thread has, and use
*that* ClassLoader instance to perform a type lookup when
JNIEnv::FindClass() fails. If ClassLoader.loadClass() then fails,
throw the exception that JNIEnv::FindClass() threw.
There's one problem with ClassLoader.loadClass(): it wants a
*Java*-convention name, *not* a JNI convention. Meaning `.`, not `/`!
// BAD
classLoader.loadClass ("java/lang/Object"); // throws
// GOOD
classLoader.loadClass ("java.lang.Object"); // throws
Consequently, we need to "fixup" the classname value to use `.`
instead of `/` before passing to ClassLoader.loadClass(). This is done
in the new JniRuntimeInfo.ToJavaName() method, which -- for *overall*
memory reduction and GC purposes -- uses a per-thread char[] cache
that is reused between calls. This avoids the need to construct
temporary StringBuilder and string instances, and should serve to
reduce overall GC pressure.
(This currently uses a 1KB cache per thread -- 512 chars -- so
hopefully it won't be *too* problematic...)
Unfortunately, there's no way to test this crazy behavior on the
desktop JVM, at least not that I've yet imagined. Consequently, the
only test for this behavior is in monodroid/tests/runtime.
[0]:
Java.Lang.NoClassDefFoundError: md5ec473bf3e64a9dd1c666612e93870662/MyCb --->
Java.Lang.ClassNotFoundException: Didn't find class "md5ec473bf3e64a9dd1c666612e93870662.MyCb" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib]]
[1]: Note that only `.` and `/system/lib` are present. Notably absent
are the application directories, e.g.
DexPathList[
[zip file "/data/app/Xamarin.Android.RuntimeTests-2.apk",
zip file "/data/app/Xamarin.Android.RuntimeTests-2.apk"],
nativeLibraryDirectories=[/data/app-lib/Xamarin.Android.RuntimeTests-2, /data/app-lib/Xamarin.Android.RuntimeTests-2, /system/lib]]
0 commit comments