Android上でJNIを使用してC++クラスからJava関数を呼び出そうとしています。私は検索して検索しましたが、私の正確な事例は見つかりませんでした。私はJavaから私のC++ライブラリのメソッドを呼び出すことができますが、その逆を行う問題があります。私は今、2日間それを混乱させて時間を無駄にしているので、誰かが私を助けてくれるよりも知識がありますか?C++クラスクラッシュからJavaへのJNIコールバック
完全な目標:JNIEnvを保持するか、後でC++クラスメソッド(JNI EXPORTではなく)で使用するためにJavaからのネイティブC++ JNI EXPORT呼び出しに渡されるJavaVM(後で有効なJNIEnvを取得して接続する)。
したがって、JavaクラスメソッドはネイティブのC++メソッドを呼び出し、そのJNIEnv *とjobjectを渡します。それらを静的クラスメンバとしてC++クラスに格納します。その後、そのC++クラスのメソッドは、それらの静的メンバーを使用して、もともとコンテキストを渡した同じクラスのJavaメソッドをコールバックします。
私はenv-> NewGlobalRef(someObj)を使ってみました。しかし、それは参照オブジェクトの将来の使用を成功させるが、それでもなお失敗するものがあるので奇妙です。
がここにいくつかのコードです:
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");
}
上記のすべてが正常に動作します、しかし、同じことをやろうとC++は、しません。 (注:私はクラスとしての私のネイティブライブラリ内のクラスを必要とし、静的呼び出しをスタンドアロンではない)
C++コード: (注:簡単にするために、ここですべてが公開されている)
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);
}
}
私はafを試したさまざまなアプローチがありますが、すべてがクラッシュします。私はDeleteGlobalRef()を使ってもそれを行いますが、それ以前にはクラッシュします。任意の洞察力が
EDIT#1評価されて:マイケルの提案を1として を、私はJNI_OnLoad関数を実装しましたし、そこからわずかのJavaVM *をキャッシュしています。 MyClass :: someCall(std :: string)メソッドの内部でJavaVMを使用してJNIEnvを取得し、env-> FindClassを使用してjclassオブジェクトを初期化し、something(String)JavaメソッドのmethodIDを取得します。 CallVoidMethodを使用したJavaでは、クラッシュが発生します。 MyClass.cpp内でextern "C" として定義
のOnLoad:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void* reserved)
{
MyClass::jvmRef = jvm;
return JNI_VERSION_1_6;
}
更新のMyClass :: someCall定義:
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();
}
あなたは 'JNIEnv'ポインタをキャッシュすることはできません。 'JavaVM'ポインタは安全にキャッシュすることができます。 'JNI_OnLoad'にあります。そして、 'JavaVM *'を使って 'GetEnv' /' AttachCurrentThread'を使って 'JNIEnv * 'を取得します。 – Michael
'JNIEnv *'を取得したときに、スレッドがすでにVMに接続されているかどうかを追跡する必要があることに注意してください。デタッチされている間に以前にそのスレッドで 'AttachCurrentThread'を呼び出さなかった場合は、' DetachCurrentThread'を呼び出さないでください。 – Michael
私はスーパーシンプルなJNI_OnLoad()を実装し、その中にjvmをキャッシュしています。私はMyClass :: someCall(std :: string msg)の中でJNI_Envを取得し、FindClassでjclassを見つけ、methodidを取得しますが、CallVoidMethodはクラッシュします。 – ErnieB