J'essaie d'appeler une fonction Java à partir d'une classe C++ en utilisant JNI sur Android. J'ai cherché et cherché mais n'ai pas trouvé mon cas exact. Je peux appeler des méthodes dans ma bibliothèque C++ de Java, mais j'ai des problèmes à faire l'inverse. Je me suis trompé pendant deux jours et je perds du temps, alors quelqu'un d'autre pourrait-il mieux savoir que moi pour m'aider? Objectif complet: Conserver le JNIEnv OU simplement le JavaVM (pour obtenir et attacher un JNIEnv valide plus tard) passé à un appel C++ JNI EXPORT de Java pour une utilisation ultérieure par une méthode de classe C++ (pas un EXPORT JNI). Ainsi, la méthode de classe Java appelle la méthode C++ native, en passant son JNIEnv * et jobject. Stockez-les en tant que membres de classe statiques dans une classe C++. Plus tard, une méthode de cette classe C++ utilise ces membres statiques pour rappeler une méthode Java de la même classe qui a passé son contexte ou quoi que ce soit d'autre.JNI callback to Java à partir de la classe C++ crash
J'ai essayé d'utiliser env-> NewGlobalRef (someObj); mais c'est étrange parce que cela fera en sorte que certaines utilisations futures des objets de référence réussissent, mais certaines échouent toujours.
Voici quelques code:
code Java:
//this is what I want to call from native code
public void something(String msg)
{
//do something with msg
}
public void somethingElse()
{
callNative();
}
private native void callNatve();
//access native
static
{
System.loadLibrary("someLib");
}
Tous les travaux ci-dessus bien, C++ essayer de faire la même chose cependant, ne fonctionne pas. (Note: J'ai besoin de la classe dans ma bibliothèque native en tant que classe et pas autonome appels statiques)
code C++: (Note: pour plus de simplicité ici tout est public)
MyClass.h:
#include <string>
#include <jni.h>
class MyClass
{
//ctor
//dtor
void someCall(std::string)
static JNIEnv* envRef;
static JavaVM* jvmRef;
static jobject objRef;
};
/////////////////////////////////////////////// /////////////////////////////////////// MyClass.cpp
#include <MyClass.h>
//static members
MyClass:;:JNIEnv* envRef;
MyClass::JavaVM* jvmRef;
MyClass::jobject objRef;
//this is the method whose instructions are crashing
void MyClass::someCall(std::string msg)
{
//works assuming i call env->NewGlobalRef(MyClass::objRef) or setup/reattach from jvm in exported call or here
jstring passMsg = envRef->NewStringUTF(msg.c_str());
clsRef = envRef->GetObjectClass(objRef);
if(clsRef == NULL)
{
return;
}
//This doesn't cause crash, but if I call FindClass("where/is/MyClass"); it does... strange
jmethodID id = envRef->GetMethodID(clsRef, "something", "(Ljava/lang/String;)V");
if(id == NULL)
{
return;
}
//Crashes
//envRef->CallVoidMethod(clsRef, id, passMsg);
if(envRef->ExceptionCheck())
{
envRef->ExceptionDescribe();
}
//Also crashes
//jvmRef->DetachCurrentThread();
}
//this works
extern "C"
{
JNIEXPORT void JNICALL Java_com_my_project_class_callNative(JNIEnv* env, jobject obj)
{
MyClass::objRef = env->NewGlobalRef(obj);
MyClass::envRef = env;
//tried both
//MyClass::envRef->GetJavaVM(&MyClass::jvmRef);
env->GetJavaVM(&MyClass::jvmRef);
//Tried this
/*
int envStat = MyClass::jvmRef->GetEnv((void**)&MyClass::envRef, JNI_VERSION_1_6);
if(envStat == JNI_EDETACHED)
{
//TODO: LOG
//std::cout << "GetEnv: not attached" << std::endl;
if(MyClass::jvmRef->AttachCurrentThread(&MyClass::envRef, NULL) != 0)
{
//TODO: LOG
//std::cout << "Failed to attach" << std::endl;
}
}else if(envStat == JNI_OK)
{
//
}else if(envStat == JNI_EVERSION)
{
//TODO: LOG
//std::cout << "GetEnv: version not supported" << std::endl;
}
*/
//calling detachcurrentthread here crashes if set above
MyClassObj.someCall(an std::string);
}
}
J'ai essayé af eW différentes approches, mais ils causent tous des accidents. Je fais aussi DeleteGlobalRef() quand je l'utilise, mais il se bloque bien avant. Toute idée est appréciée
EDIT # 1: Selon la suggestion de Michael, j'ai mis en place la fonction JNI_OnLoad et ont mis en cache tout le JavaVM * à partir de là. Dans la méthode MyClass :: someCall (std :: string), j'utilise ensuite JavaVM pour obtenir JNIEnv, initialiser un objet jclass en utilisant env-> FindClass et obtenir l'identificateur de méthode pour la méthode java (String), mais en essayant de rappeler Java avec CallVoidMethod entraîne un crash immobile.
OnLoad défini comme extern "C" dans MyClass.cpp:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void* reserved)
{
MyClass::jvmRef = jvm;
return JNI_VERSION_1_6;
}
Mis à jour MyClass :: someCall Définition:
void MyClass::someCall(std::string msg)
{
//Get environment from cached jvm
JNIEnv* env;
jclass cls;
int envStat = MyClass::jvmRef->GetEnv((void**)&env, JNI_VERSION_1_6);
bool attached = false;
if(envStat == JNI_EDETACHED)
{
//TODO: LOG
if(JavaInterface::jvmRef->AttachCurrentThread(&env, NULL) != 0)
{
//TODO: LOG
// "Failed to attach"
return;
}else if(envStat == JNI_OK)
{
attached = true;
}else if(envStat == JNI_EVERSION)
{
//TODO: LOG
// "GetEnv: version not supported"
}
}
cls = env->FindClass("package/location/project/JavaClass");
if(cls == NULL)
{
//TODO: LOG
return;
}
jmethodID id = env->GetMethodID(cls, "something", "(Ljava/lang/String;)V");
if(id == NULL)
{
return;
}
jstring passMsg = env->NewStringUTF(msg.c_str());
//Crashes
env->CallVoidMethod(cls, id, passMsg);
if(attached)
jvmRef->DetachCurrentThread();
}
Vous n'êtes pas censé mettre en cache les pointeurs 'JNIEnv'. Le pointeur 'JavaVM' est sûr de mettre en cache, donc vous pouvez le faire par exemple. dans 'JNI_OnLoad'. Et puis vous utilisez ce 'JavaVM *' pour obtenir un 'JNIEnv *' en utilisant 'GetEnv' /' AttachCurrentThread'. – Michael
Notez que lorsque vous obtenez le 'JNIEnv *', vous devez garder une trace si le thread était déjà attaché à la machine virtuelle. Parce que vous ne devez pas appeler 'DetachCurrentThread', sauf si vous avez précédemment appelé' AttachCurrentThread' sur ce thread pendant qu'il était dérouté. – Michael
Ok, j'ai implémenté un super simple JNI_OnLoad() et cache le jvm dedans. Je peux obtenir le JNI_Env de cela alors que dans MyClass :: someCall (std :: string msg), trouver le jclass bien avec FindClass, et obtenir la méthode, mais CallVoidMethod se bloque toujours. – ErnieB