簡體   English   中英

從C ++類崩潰到Java的JNI回調

[英]JNI callback to Java from c++ class crashing

我正在嘗試在Android上使用JNI從C ++類調用Java函數。 我進行了搜索,但沒有找到確切的案例。 我可以從Java調用c ++庫中的方法,但是在執行相反操作時會遇到問題。 我已經把它弄亂了兩天,浪費時間,所以有誰比我更能幫助我?

完整目標:保留JNIEnv,或者僅保留JavaVM(以便以后獲取並附加有效的JNIEnv),並從Java傳遞到本機c ++ JNI EXPORT調用,以供以后由c ++類方法(而不是JNI EXPORT)使用。

因此,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);
    }
}

我嘗試了幾種不同的方法,但是它們都會導致崩潰。 我也使用DeleteGlobalRef(),但是在此之前它崩潰了。 任何見解表示贊賞

編輯#1:按照邁克爾的建議,我已經實現了JNI_OnLoad函數,並從那里僅緩存了JavaVM *。 在MyClass :: someCall(std :: string)方法內部,然后使用JavaVM獲取JNIEnv,使用env-> FindClass初始化jclass對象,並獲取something(String)java方法的methodID,但是嘗試回調到帶有CallVoidMethod的Java仍然導致崩潰。

在MyClass.cpp中,將OnLoad定義為extern“ C”:

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更改后的錯誤是我將錯誤的對象傳遞給CallVoidMethod()。 我最終要做的是,將通過callNative(JNIEnv * env,jobject obj)傳遞的Jobject作為MyClass的靜態成員存儲,並將其傳遞給CallVoidMethod而不是cls。

較早的通話:

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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM