Android에서 JNI를 사용하여 C++ 클래스에서 Java 함수를 호출하려고합니다. 나는 수색하고 찾았지만 정확한 사건을 발견하지 못했다. Java에서 내 C++ 라이브러리의 메서드를 호출 할 수 있지만 그 반대로 수행하는 데 문제가 있습니다. 나는 지금 이틀 동안 그것을 엉망으로 만들었고 시간을 낭비하고있다. 그래서 내가 도와주는 것보다 더 많은 지식을 가질 수 있을까?C++ 클래스 충돌에서 JNI 콜백
전체 목표 : 나중의 C++ 클래스 메소드 (JNI 내보내기가 아님)에서 사용할 수 있도록 Java에서 네이티브 C++ JNI EXPORT 호출에 전달 된 JNIEnv 또는 JavaVM 만 유지하거나 (유효한 JNIEnv 가져 오기 및 첨부).
따라서 Java 클래스 메소드는 JNIEnv * 및 jobject를 전달하여 원시 C++ 메소드를 호출합니다. 정적 클래스 멤버로 C++ 클래스에 저장합니다. 나중에, 그 C++ 클래스의 메소드는 정적 인 멤버를 사용하여 원래 컨텍스트를 통과 한 클래스와 같은 클래스의 Java 메소드를 콜백합니다.
나는 env-> NewGlobalRef (someObj)를 사용해 보았습니다. 그러나 참조 객체의 향후 사용을 성공하게 만들지 만 일부 객체는 여전히 실패하기 때문에 이상합니다.
자바 코드 : 위의
//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는 ++,하지 않습니다는 여기에 몇 가지 코드입니다. (참고 : 편의상 여기에 모든 것이 공개)
에서 MyClass.h :
#include <string>
#include <jni.h>
class MyClass
{
//ctor
//dtor
void someCall(std::string)
static JNIEnv* envRef;
static JavaVM* jvmRef;
static jobject objRef;
};
C++ 코드 (참고 : 나는 클래스로 내 네이티브 라이브러리에있는 클래스가 필요하고 정적 호출을 독립되지 않음)
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////// 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()를하지만 너무 길다. 어떤 통찰력이
편집 # 1 감사합니다 : 마이클의 제안에 따라 경찰 을, 나는주는 JNI_OnLoad 기능을 구현했습니다과의 JavaVM * 거기에서 단지 캐시있다. MyClass :: someCall (std :: string) 메서드 내에서 JavaVM을 사용하여 JNIEnv를 가져오고 env-> FindClass를 사용하여 jclass 객체를 초기화하고 something (String) java 메서드에 대한 methodID를 가져 오지만 콜백을 시도합니다. CallVoidMethod가있는 Java는 여전히 충돌이 발생합니다. 인 MyClass.cpp에서 통근자 "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