如何从C中调用Java函数

4

我遇到了一个问题,需要从c/c++中调用Java函数。

在示例和教程中,我只看到一个Java应用程序调用C方法,并在该方法中调用另一个Java方法,但我想做的是从代码的任何部分调用Java方法。这是我的代码:

static JNIEnv mEnv;
static jclass mClassAndroidActivity;
static mMethodSayHello;
JNIEXPORT void JNICALL JNI_FUNCTION(AndroidActivity_nativeInit)(JNIEnv* env, jobject obj, int width, int height)
{
    mEnv = env;
    jclass cls = (*env)->GetObjectClass(env, obj);
    mClassAndroidActivity = (*env)->NewGlobalRef(env, cls);
    mMethodSayHello = (*env)->GetMethodID (env, mClassAndroidActivity, "SayHello", "(Ljava/lang/String;)V");
}

//this method is called from a cpp
void nativeSayHello(char* msg)
{
    jstring string = (*mEnv)->NewStringUTF(mEnv, msg);
    (*mEnv)->CallVoidMethod(mEnv, mClassAndroidActivity, mMethodSayHello, string);
}

但是总是崩溃,我已经尝试过不使用NewGlobalRef,在JNI_Function中使用mEnv而不是env,我尝试从JNI_OnLoad中获取方法ID,但总是崩溃。

这是我得到的日志:

02-15 18:09:48.520: W/dalvikvm(27904): JNI警告:threadid=1正在使用来自threadid=0的env


我得到的“最佳”日志显示,线程1的环境与线程0不同。我相信这是正确的。每个Java线程应该有一个不同的env指针。 - Yourpalal
是的,但我该如何保存第一个环境指针,以便在不同的线程中使用? - cbedoya
1个回答

9

您不能重复使用JNIEnv,因为它是特定于调用线程的。要从本地代码调用(非静态)Java方法,您需要像这样的东西:

static JavaVM *gJavaVM;
static jobject gCallbackObject = NULL;

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    gJavaVM = vm;
    return JNI_VERSION_1_6;
}

JNIEXPORT void JNICALL JNI_FUNCTION(AndroidActivity_nativeInit)(JNIEnv* env, jobject obj, int width, int height) {
    // ...
    gCallbackObject = (*env)->NewGlobalRef(env, obj);
}

JNIEXPORT void JNICALL JNI_FUNCTION(AndroidActivity_nativeRelease)(JNIEnv* env, jobject obj) {
    (*env)->DeleteGlobalRef(env, gCallbackObject);
    gCallbackObject = NULL;
}

//this method is called from native code
void nativeSayHello(char* msg) {
    int status;
    JNIEnv *env;
    int isAttached = 0;

    if (!gCallbackObject) return;

    if ((status = (*gJavaVM)->GetEnv(gJavaVM, (void**)&env, JNI_VERSION_1_6)) < 0) {
        if ((status = (*gJavaVM)->AttachCurrentThread(gJavaVM, &env, NULL)) < 0) {
            return;
        }
        isAttached = 1;
    }

    jclass cls = (*env)->GetObjectClass(env, gCallbackObject);
    if (!cls) {
        if (isAttached) (*gJavaVM)->DetachCurrentThread(gJavaVM);
        return;
    }

    jmethodID method = (*env)->GetMethodID(env, cls, "SayHello", "(Ljava/lang/String;)V");
    if (!method) {
        if (isAttached) (*gJavaVM)->DetachCurrentThread(gJavaVM);
        return;
    }

    jstring string = (*mEnv)->NewStringUTF(mEnv, msg);
    (*env)->CallVoidMethod(env, gCallbackObject, method, string);

    if (isAttached) (*gJavaVM)->DetachCurrentThread(gJavaVM);
}

这段代码片段未经过测试。为了防止内存泄漏,请在您的Java代码中不再需要该对象的引用时调用nativeRelease()方法。

有关更多详细信息,请参见Java Native Interface文档


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