#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
, вы должны:
- убедитесь, что вы используете конкретный поток
env
(полученный путем вызоваAttachCurrentThread
виртуальной машины, если это необходимо).; - убедитесь, что все необходимые ссылки на классы были получены заранее. Это может быть так же просто, как вызвать
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.