FindClass à partir de n’importe quel thread dans Android JNI

La page des astuces JNI d’Android mentionne cette FAQ: Pourquoi FindClass n’a-t-il pas trouvé ma classe? Ils mentionnent plusieurs solutions et la dernière option est celle-ci:

Mettre en cache une référence à l’object ClassLoader à un endroit pratique et émettre des appels loadClass directement. Cela demande un certain effort.

Donc, j’ai essayé de le faire fonctionner et il semble que peu importe, cette méthode ne fonctionne tout simplement pas pour moi. Finalement, j’ai compris comment utiliser ClassLoader mais ça ne marchera pas si, à partir d’un thread natif, j’essaie de charger une classe qui n’a pas encore été touchée / chargée. Essentiellement, il est identique au comportement env-> FindClass lorsqu’il est appelé depuis un thread natif, à l’exception qu’il ne renverra pas 0 pour les classes déjà utilisées dans l’application. N’importe quelle idée si je n’ai pas bien compris, ou il est impossible d’accéder aux classes à partir d’un thread natif qui n’a pas encore été utilisé / chargé.

EDIT: Je vais donner plus d’informations pour expliquer ce que je veux dire exactement. Il y a JNI env->FindClass(className) , et un autre que j’ai écrit myFindClass(env, className) qui utilise ClassLoader->loadClass cache.

La classe à laquelle j’essaie d’accéder à partir de c / c ++ natif est “com / noname / TestClient”. Dans myFindClass, j’utilise également env-> FindClass et la valeur de log qu’il renvoie:

 jclass myFindClass(JNIEnv * env, const char* name) { ... jclass c0 = env->FindClass(name); jclass c1 = (jclass)env->CallObjectMethod(ClassLoader, MID_loadClass, envNewSsortingngUTF(name)); dlog("myFindClass(\"%s\") => c0:%p, c1:%p, c0 and c1 are same: %d", name, c0, c1, env->IsSameObject(c0, c1)); ... } 

Ensuite, j’ai ces 3 combinaisons pour expliquer le problème.

1)

 //inside JNI_OnLoad thread myFindClass(env, "com/noname/TestClient"); ... //inside native thread created by pthread_create myFindClass(env, "com/noname/TestClient"); 

Je reçois ce logcat:

myFindClass (“com / noname / TestClent”) => c0: 0x41b64558, c1: 0x41b64558, c0 et c1 sont identiques: 1

myFindClass (“com / noname / TestClent”) => c0: 0, c1: 0x41b64558, c0 et c1 sont identiques: 0

2)

 //inside JNI_OnLoad thread env->FindClass("com/noname/TestClient"); ... //inside native thread created by pthread_create myFindClass("com/noname/TestClient"); 

Je reçois ce logcat:

myFindClass (“com / noname / TestClent”) => c0: 0, c1: 0x41b64558, c0 et c1 sont identiques: 0

3)

 //inside JNI_OnLoad thread //"com/noname/TestClient" isn't touched from JNI_OnLoad. ... //inside native thread created by pthread_create myFindClass(env, "com/noname/TestClient"); 

Je reçois ce logcat:

myFindClass (“com / noname / TestClent”) => c0: 0, c1: 0, c0 et c1 sont identiques: 1

Fondamentalement, mon problème est que ClassLoader ne trouve pas ma classe dans le 3ème cas. Est-ce un bug? Que peut-on faire pour résoudre le problème?

EDIT2: En plus de cela, il semble que ClassLoader :: loadClass est clairement bogué. Si je demande myFindClass (“noname / TestClent”), il retourne des ordures, et lorsque j’utilise ce retour, jclass se bloque de quelque manière que ce soit.

Après avoir essayé et écrasé mon application, un collègue et moi avons réussi à mettre en cache et à utiliser avec succès le chargeur de classes dans un autre thread natif. Le code que nous avons utilisé est indiqué ci-dessous (C ++ 11, mais facilement converti en C ++ 2003), publié ici car nous n’avons trouvé aucun exemple de ce qui précède “Mettre en cache une référence à l’object ClassLoader quelque part, et émettre loadClass appelle directement. Cela nécessite un certain effort. “. L’appel de findClass a parfaitement fonctionné lorsqu’il a été appelé depuis un thread différent de celui de JNI_OnLoad. J’espère que ça aide.

 JavaVM* gJvm = nullptr; static jobject gClassLoader; static jmethodID gFindClassMethod; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) { gJvm = pjvm; // cache the JavaVM pointer auto env = getEnv(); //replace with one of your classes in the line below auto randomClass = env->FindClass("com/example/RandomClass"); jclass classClass = env->GetObjectClass(randomClass); auto classLoaderClass = env->FindClass("java/lang/ClassLoader"); auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader", "()Ljava/lang/ClassLoader;"); gClassLoader = env->CallObjectMethod(randomClass, getClassLoaderMethod); gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass", "(Ljava/lang/Ssortingng;)Ljava/lang/Class;"); return JNI_VERSION_1_6; } jclass findClass(const char* name) { return static_cast(getEnv()->CallObjectMethod(gClassLoader, gFindClassMethod, getEnv()->NewSsortingngUTF(name))); } JNIEnv* getEnv() { JNIEnv *env; int status = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6); if(status < 0) { status = gJvm->AttachCurrentThread(&env, NULL); if(status < 0) { return nullptr; } } return env; } 

Essayez d’abord de joindre votre thread natif à la JVM.

Le pointeur sur jvm, vous pouvez obtenir la première chose dans JNI_OnLoad

 env->GetJavaVM(&jvm); 

Puis à partir de votre fil natif

 JNIEnv *env; jvm->AttachCurrentThread((void **)&env, NULL); 

Ensuite, utilisez cet env pour FindClass