JNI - 在C++中调用Java方法

4

我一直在尝试从C++调用Java方法,但一直没有成功。以下是收到的错误信息:

JNI错误(应用程序错误):访问过期的本地引用0x5cb00019(在大小为2的表中的索引6) 虚拟机中止 致命信号11(SIGSEGV)于0xdeadd00d(code=1)

这是我在代码(Java端)中所做的:

    public class Wrapper extends Activity{
        private native void initJNIBridge();
            static final String TAG = "Wrapper";

    protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);

            initJNIBridge(); // Calls C++ function.
    }

    public void upgradeAdFree() {
             Log.d(TAG, "Wrapper::upgradeAdFree()");
    }

以下是与C++相关的内容:

    typedef struct JniMethodInfo_
    {
        JNIEnv *    env;
        jclass      classID;
        jmethodID   methodID;
    } JniMethodInfo;

    extern "C"
    {
static jobject javaObj;

// get env and cache it
static JNIEnv* getJNIEnv(void)
{

    JavaVM* jvm = cocos2d::JniHelper::getJavaVM();
    if (NULL == jvm) {
        LOGD("Failed to get JNIEnv. JniHelper::getJavaVM() is NULL");
        return NULL;
    }

    JNIEnv *env = NULL;
    // get jni environment
    jint ret = jvm->GetEnv((void**)&env, JNI_VERSION_1_4);

    switch (ret) {
        case JNI_OK :
            // Success!
            return env;

        case JNI_EDETACHED :
            // Thread not attached

            // TODO : If calling AttachCurrentThread() on a native thread
            // must call DetachCurrentThread() in future.
            // see: http://developer.android.com/guide/practices/design/jni.html

            if (jvm->AttachCurrentThread(&env, NULL) < 0)
            {
                LOGD("Failed to get the environment using AttachCurrentThread()");
                return NULL;
            } else {
                // Success : Attached and obtained JNIEnv!
                return env;
            }

        case JNI_EVERSION :
            // Cannot recover from this error
            LOGD("JNI interface version 1.4 not supported");
        default :
            LOGD("Failed to get the environment using GetEnv()");
            return NULL;
    }
}

// get class and make it a global reference, release it at endJni().
static jclass getClassID(JNIEnv *pEnv)
{
    jclass ret = pEnv->FindClass(CLASS_NAME);
    if (! ret)
    {
        LOGD("Failed to find class of %s", CLASS_NAME);
    }

    return ret;
}
static bool getMethodInfo(JniMethodInfo &methodinfo, const char *methodName, const char *paramCode)
{
    jmethodID methodID = 0;
    JNIEnv *pEnv = 0;
    bool bRet = false;

    do 
    {
        pEnv = getJNIEnv();
        if (! pEnv)
        {
            break;
        }

        jclass classID = getClassID(pEnv);

        methodID = pEnv->GetMethodID(classID, methodName, paramCode);
        if (! methodID)
        {
            LOGD("Failed to find method id of %s", methodName);
            break;
        }

        methodinfo.classID = classID;
        methodinfo.env = pEnv;
        methodinfo.methodID = methodID;


        bRet = true;
    } while (0);

    return bRet;
}   

JNIEXPORT void JNICALL Java_org_test_games_Wrapper_initJNIBridge(JNIEnv *, jobject jobj){
    LOGD("Java_org_test_games_Wrapper_initJNIBridge()");

    javaObj = jobj;

    return;
}

void upgradeAdFreeJNI()
{      
    LOGD("upgradeAdFreeJNI");

    JniMethodInfo methodInfo;
    if (! getMethodInfo(methodInfo, "upgradeAdFree", "()V"))
    {
        LOGD("Cannot find method!");
        return;
    }

    methodInfo.env->CallVoidMethod(javaObj, methodInfo.methodID);
}


}

我已经为这个问题苦思冥想几天了,任何建议都将不胜感激,因为这并不是世界上最容易调试的任务。

谢谢!

2个回答

4
问题是您指向jobject的静态指针不再有效。 您需要保护它免受任何垃圾回收运行的影响。 这可以通过使用全局引用而不是本地引用来完成。 不要仅仅赋值指针,而是创建一个globalRef。
JNIEXPORT void JNICALL Java_org_test_games_Wrapper_initJNIBridge(JNIEnv *, jobject jobj)
{
    LOGD("Java_org_test_games_Wrapper_initJNIBridge()");

    javaObj = env->NewGlobalRef(env, jobj);

return;
}

但请注意,您还必须释放globafRef。


2

您不能仅仅存储一个指向对象的指针,正如错误所示。当您想要存储它时,您必须存储对对象的本地或全局引用。请参见http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#global_local

更改为:

    JNIEXPORT void JNICALL Java_org_test_games_Wrapper_initJNIBridge(JNIEnv *env, jobject jobj){
        LOGD("Java_org_test_games_Wrapper_initJNIBridge()");

        javaObj = NewGlobalRef(env, jobj);

        return;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接