2017-10-08 46 views
1

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(); 
} 
+1

あなたは 'JNIEnv'ポインタをキャッシュすることはできません。 'JavaVM'ポインタは安全にキャッシュすることができます。 'JNI_OnLoad'にあります。そして、 'JavaVM *'を使って 'GetEnv' /' AttachCurrentThread'を使って 'JNIEnv * 'を取得します。 – Michael

+0

'JNIEnv *'を取得したときに、スレッドがすでにVMに接続されているかどうかを追跡する必要があることに注意してください。デタッチされている間に以前にそのスレッドで 'AttachCurrentThread'を呼び出さなかった場合は、' DetachCurrentThread'を呼び出さないでください。 – Michael

+0

私はスーパーシンプルなJNI_OnLoad()を実装し、その中にjvmをキャッシュしています。私はMyClass :: someCall(std :: string msg)の中でJNI_Envを取得し、FindClassでjclassを見つけ、methodidを取得しますが、CallVoidMethodはクラッシュします。 – ErnieB

答えて

0

さて、編集#1の変更後の誤差があることが私ですCallVoidMethod()に間違ったオブジェクトを渡していました。私がやったことは、callNative(JNIEnv * env、jobject obj)によって渡されたjobjectをMyClassの静的メンバーとして格納し、それをclsの代わりにCallVoidMethodに渡すことです。

以前の呼び出し:それはもはや必要だ後

JNIEXPORT void JNICALL Java_path_to_project_JavaClass_nativeCall(JNIEnv* env, jobject obj) 
{ 
    JavaInterface::objRef = env->NewGlobalRef(obj) 
} 

DeleteGlobalRefを(OBJREF)は、他の場所で呼び出されません。次に、ネイティブのsomeCallメソッドで唯一の変更が行われました。

void MyClass::someCall(std::string msg) 
{ 
    //everything here is the same 

    //Now either of these will work 
    cls = env->FindClass("com/empsoftworks/andr3ds/NativeInterface"); 
//cls = env->GetObjectClass(objRef); 

    //unchanged stuff 

    //This is what fixed it 
    env->CallVoidMethod(objRef, id, passMsg); 
} 
+0

これは長期的にも機能しません。 'jobjects' acoss JNIメソッド呼び出しを保存することはできません。 'GlobalRef'が必要です。あなたはあまり読んでいないようです。私はあなたがJNIリファレンスを勉強することをお勧めします。それのすべて。 – EJP

+0

上記は編集されており、実際に動作します。 – ErnieB

+0

それは私が言ったことです。 'GlobalRef'を使う必要があります。そうしないと長期的には動作しません。 – EJP

関連する問題