NewGlobalRef called with pending exception java.lang.ClassNotFoundException on different .so

Viewed 430

I have a lib my_app.so which uses my_app_helper.so.

The line

jclass jc = env->FindClass("com.my_app.flutter_app.MainActivity");

works perfectly on any place on my_app.so, but gives:

A/zygote64: java_vm_ext.cc:523] JNI DETECTED ERROR IN APPLICATION: JNI 
NewGlobalRef called with pending exception java.lang.ClassNotFoundException: 
Didn't find class "com.my_app.flutter_app.MainActivity" on path: 
DexPathList[[dex file "InMemoryDexFile[cookie=[0, 
547409613216]]"],nativeLibraryDirectories=[/system/lib64, /system/vendor/lib64]]

on my_app_helper.so

My JNI_Onload is on my_app.so, and it passes the JavaVM *vm to my_app_helper.so.

Why it does not find the classes when called from inside my_app_helper.so?

UPDATE:

Here's the backtrace:

A/zygote64: java_vm_ext.cc:523] JNI DETECTED ERROR IN APPLICATION: JNI NewGlobalRef called with pending exception java.lang.ClassNotFoundException: Didn't find class "com.my_app.flutter_app.MainActivity" on path: DexPathList[[dex file "InMemoryDexFile[cookie=[0, 547694828128]]"],nativeLibraryDirectories=[/system/lib64, /system/vendor/lib64]]
    java_vm_ext.cc:523]   at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:93)
    java_vm_ext.cc:523]   at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379)
    java_vm_ext.cc:523]   at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
    java_vm_ext.cc:523]   at java.lang.Object com.rmsl.juce.JuceInvocationHandler.dispatchInvoke(long, java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) (JuceInvocationHandler.java:-2)
    java_vm_ext.cc:523]   at java.lang.Object com.rmsl.juce.JuceInvocationHandler.invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) (JuceInvocationHandler.java:28)
    java_vm_ext.cc:523]   at java.lang.Object java.lang.reflect.Proxy.invoke(java.lang.reflect.Proxy, java.lang.reflect.Method, java.lang.Object[]) (Proxy.java:913)
    java_vm_ext.cc:523]   at void android.app.Application$ActivityLifecycleCallbacks.onActivityStarted(android.app.Activity) ((null):-1)
    java_vm_ext.cc:523]   at void android.app.Application.dispatchActivityStarted(android.app.Activity) (Application.java:207)
    java_vm_ext.cc:523]   at void android.app.Activity.onStart() (Activity.java:1249)
    java_vm_ext.cc:523]   at void io.flutter.embedding.android.FlutterActivity.onStart() (FlutterActivity.java:533)
    java_vm_ext.cc:523]   at void com.my_app.flutter_app.MainActivity.onStart() (MainActivity.kt:256)
    java_vm_ext.cc:523]   at void android.app.Instrumentation.callActivityOnStart(android.app.Activity) (Instrumentation.java:1355)
    java_vm_ext.cc:523]   at void android.app.Activity.performStart() (Activity.java:7001)
    java_vm_ext.cc:523]   at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2807)
    java_vm_ext.cc:523]   at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2923)
    java_vm_ext.cc:523]   at void android.app.ActivityThread.-wrap11(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
    java_vm_ext.cc:523]   at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1616)
    java_vm_ext.cc:523]   at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:105)
    java_vm_ext.cc:523]   at void android.os.Looper.loop() (Looper.java:164)
    java_vm_ext.cc:523]   at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6617)
    java_vm_ext.cc:523]   at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
    java_vm_ext.cc:523]   at void com.android.internal.os.Zygote$MethodAndArgsCaller.run() (Zygote.java:240)
    java_vm_ext.cc:523]   at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:769)
    java_vm_ext.cc:523] 
    java_vm_ext.cc:523]     in call to NewGlobalRef
    java_vm_ext.cc:523]     from java.lang.Object com.rmsl.juce.JuceInvocationHandler.dispatchInvoke(long, java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
    java_vm_ext.cc:523] "main" prio=5 tid=1 Runnable
    java_vm_ext.cc:523]   | group="main" sCount=0 dsCount=0 flags=0 obj=0x72a3f710 self=0x7f93ec3a00
    java_vm_ext.cc:523]   | sysTid=21008 nice=-10 cgrp=default sched=0/0 handle=0x7f986549b0
    java_vm_ext.cc:523]   | state=R schedstat=( 583813855 313733548 562 ) utm=46 stm=11 core=1 HZ=100
    java_vm_ext.cc:523]   | stack=0x7ff863b000-0x7ff863d000 stackSize=8MB
    java_vm_ext.cc:523]   | held mutexes= "mutator lock"(shared held)
A/zygote64: java_vm_ext.cc:523]   native: #00 pc 00000000003982fc  /system/lib64/libart.so (_ZN3art15DumpNativeStackERNSt3__113basic_ostreamIcNS0_11char_traitsIcEEEEiP12BacktraceMapPKcPNS_9ArtMethodEPv+212)
1 Answers

You have not given us much to work with, but I think your problem stems from this Android JNI FAQ:

You can get into trouble if you create a thread yourself (perhaps by calling pthread_create and then attaching it with AttachCurrentThread). Now there are no stack frames from your application. If you call FindClass from this thread, the JavaVM will start in the "system" class loader instead of the one associated with your application, so attempts to find app-specific classes will fail.

So, in the code for my_app_helper.so, you should:

  1. make sure to use a thread-specific env (obtained by calling AttachCurrentThread on the vm if needed);
  2. make sure that all class references you need were obtained in advance. This can be as easy as calling FindClass from my_app.so and storing a GlobalReference in a static/global variable. (g_my_class = env->NewGlobalRef(env->FindClass("..."))) This is the easiest workaround, but see the linked post for other options.

In general, you should practice good JNI hygiene and always check for errors after a JNI call, and for Android specifically, look into turning on extended checking while developing.

EDIT: There is a second case in which FindClass will always work:

Any FindClass calls made as part of executing JNI_OnLoad will use the class loader associated with the function that called System.loadLibrary (this is a special rule, provided to make library initialization more convenient)."

You can see this in the Android Runtime source code here:

    // **: class_loader is the class loader of the code that called `loadLibrary`
    ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
    self->SetClassLoaderOverride(class_loader); // **

    VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
    using JNI_OnLoadFn = int(*)(JavaVM*, void*);
    JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
    int version = (*jni_on_load)(this, nullptr);

    self->SetClassLoaderOverride(old_class_loader.get()); // **
Related