NewGlobalRef вызывается с ожидающим исключением java.lang.Исключение ClassNotFoundException для разных .так что

#android #c #java-native-interface

Вопрос:

У меня есть библиотека my_app.so , которая использует my_app_helper.so .

Линия

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

отлично работает в любом месте my_app.so , но дает:

 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]]
 

на my_app_helper.so

Мой JNI_Onload включен my_app.so , и он передает JavaVM *vm my_app_helper.so «кому».

Почему он не находит классы при вызове изнутри my_app_helper.so ?

Обновить:

Вот обратный путь:

 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. Из какой темы вы звоните env->FindClass ? Как выглядит стек вызовов?

2. @esentsov Я обновил с обратной связью

3. Разве это не должен быть звонок env->FindClass("com/my_app/flutter_app/MainActivity") ? Кроме того, вы делаете это из потока, который вы сами создали pthread_new , или аналогичного?

4. @Botje да, все происходит в новой теме. Не та нить, которую я контролирую, но они есть. Должен ли я прикрепить нить? Я точно не знаю, но я думаю, что есть что-то в том, чтобы прикрепить нить к чему-то. Я попробовал это сделать, если я правильно помню, и это не сработало. И да, так и должно быть com/my_app/flutter_app/MainActivity , я набрал неправильно, но я правильно написал код.

Ответ №1:

Вы не дали нам много для работы, но я думаю, что ваша проблема связана с этим часто задаваемым вопросом о JNI для Android:

Вы можете попасть в беду, если создадите поток самостоятельно (возможно, вызвав pthread_create, а затем прикрепив его с помощью AttachCurrentThread). Теперь в вашем приложении нет стековых кадров. Если вы вызовете FindClass из этого потока, JavaVM запустится в загрузчике классов «система», а не в загрузчике, связанном с вашим приложением, поэтому попытки найти классы для конкретного приложения завершатся неудачей.

Итак, в коде для my_app_helper.so , вы должны:

  1. убедитесь, что вы используете конкретный поток env (полученный путем вызова AttachCurrentThread виртуальной машины, если это необходимо).;
  2. убедитесь, что все необходимые ссылки на классы были получены заранее. Это может быть так же просто, как вызвать FindClass my_app.so и сохранить глобальную ссылку в статической/глобальной переменной. ( g_my_class = env->NewGlobalRef(env->FindClass("...")) ) Это самый простой обходной путь, но другие варианты см. в связанной публикации.

В общем, вам следует соблюдать гигиену JNI и всегда проверять наличие ошибок после вызова JNI, а для Android, в частности, рассмотрите возможность включения расширенной проверки во время разработки.

ИЗМЕНИТЬ: Есть второй случай, в котором FindClass всегда будет работать:

Любые вызовы FindClass, выполняемые в рамках выполнения JNI_OnLoad, будут использовать загрузчик классов, связанный с функцией, которая вызвала System.LoadLibrary (это специальное правило, предусмотренное для более удобной инициализации библиотеки).»

Вы можете увидеть это в исходном коде среды выполнения Android здесь:

     // **: 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()); // **
 

Комментарии:

1. К сожалению AttachCurrentThread , не работает. Действительно ли необходимо кэширование всех классов из основного потока или, еще лучше, из JNI_Onload него?

2. Вам нужно сделать и то, и другое: совместное использование JNIEnv в потоках запрещено, и JVM Android не поддерживает FindClass в потоках, которые он не создавал сам. Смотрите ссылку часто задаваемых вопросов о том, почему.

3. Я подозревал, что не смогу использовать FindClass в другом потоке, поэтому я сделал этот минимальный пример, и он сработал: pastebin.com/WjfnFaDX . Ты знаешь, почему? FindCLass вызывается из другого потока

4. Интересный. Это может быть связано со следующей формулировкой в FAQ: «Любые вызовы FindClass, выполняемые в рамках выполнения JNI_OnLoad, будут использовать загрузчик классов, связанный с функцией, которая вызвала System.LoadLibrary (это специальное правило, предусмотренное для более удобной инициализации библиотеки)». Для меня это звучит как «мы переопределяем системный загрузчик классов во время работы JNI_OnLoad». Однако у меня нет времени копаться в исходном коде произведений искусства :/

5. Нашел его! : у виртуальной машины переопределен корневой загрузчик классов во время работы JNI_OnLoad. Я ожидаю, что ваш эксперимент не сработает, если лямбда запустится после возврата JNI_OnLoad.