使用 JNI 从本机 C 中的不同函数调用 Java - 引用丢失?

发布于 2025-01-16 20:48:05 字数 2114 浏览 5 评论 0原文

我正在修改一个用本机 C 编写的现有库,以使其能够在 Android 上运行。为了访问一些硬件元素,我需要使用一些 Java 函数。因此,我使用 JNI 从本机 C 调用 Java 函数。

我的方法如下:

  1. 使用 JNI_OnLoad 获取对 Java VM 的引用。
  2. 获取 JNI 环境 (JNIEnv)
  3. 使用 FindClass 查找 Java 类
  4. 使用 NewObject 创建 Java 类的实例
  5. 创建对我需要调用的 Java 方法的引用。

随后,我调用了另一个函数,该函数实际上使用之前存储的引用来调用 Java 方法。所有对象和引用都存储在全局变量中,以便在 JNI_OnLoad 上初始化它们并在附加函数中使用它们。

问题是,当我调用实际的 Java 方法时,我总是得到 0(它应该返回一个 int)。如果我检查异常,似乎有一个异常,但我无法确切地看到是什么创建了异常。

如果我从 JNI_OnLoad 函数调用该方法,那么它就可以工作。这让我觉得当 JNI_OnLoad 函数退出时,一些引用会丢失。我正在将所有对象转换为全局引用,但肯定还有一些东西丢失了。

这是我的设置的一个最小示例。任何帮助将不胜感激。

谢谢!

JC

Native C:

JNIEnv   *jni_env = NULL;
jclass    jni_cls = NULL;
jobject   jni_obj = NULL;
jmethodID jni_myJavaFunc= NULL;

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    (void)reserved;

    jint ver = JNI_VERSION_1_6;

    if (vm == NULL)
        return JNI_ERR;

    if ((*vm)->GetEnv(vm, (void **)&jni_env, ver) != JNI_OK)
        return JNI_ERR;
        
    jni_cls = (jclass)(*jni_env)->NewGlobalRef(jni_env, (*jni_env)->FindClass(jni_env, "org/java/MyJavaClass"));

    if (jni_cls == NULL)
        return JNI_ERR;

    jmethodID init = (*jni_env)->GetMethodID(jni_env, jni_cls, "<init>", "()V");

    if (init == NULL)
        return JNI_ERR;

    jni_obj = (*jni_env)->NewGlobalRef(jni_env, (*jni_env)->NewObject(jni_env, jni_cls, init));

    if (jni_obj == NULL)
        return JNI_ERR;

    jni_myJavaFunc = (*jni_env)->GetMethodID(jni_env, jni_cls, "myJavaFunc", "()I");

    return ver;
}

int myNativeFunc()
{
    if (jni_myJavaFunc != NULL) {
    
        (*jni_env)->ExceptionClear(jni_env);

        int n = (*jni_env)->CallIntMethod(jni_env, jni_obj, jni_myJavaFunc);
        
        if (!(*jni_env)->ExceptionCheck(jni_env))
            return n;
        else
            return -1;
    }
}

Java类:

public class MyJavaClass extends Activity
{
    public MyJavaClass()
    {

    }
    
    public int myJavaFunc()
    {
        return 1;
    }
}

I am modifying an existing library written in native C to get it to work on Android. To access some hardware elements, I need to use some Java functions. So, I'm using JNI to call Java functions from native C.

My approach is the the following:

  1. Use JNI_OnLoad to get a reference to the Java VM.
  2. Get the JNI environment (JNIEnv)
  3. Find the Java class using FindClass
  4. Create an instance of the Java class using NewObject
  5. Create a reference to the Java method I need to call.

Later on, I call a different function which actually calls the Java method using the previously stored reference. All objects and references are stored in global variables in order to initialize them on JNI_OnLoad and use them at the additional function.

The problem is that when I call the actual Java method I always get 0 (it should return an int). If I check for exceptions, it seems there is one but but I cannot see exactly what is creating the exception.

If I call the method from the JNI_OnLoad function, then it works. This makes me think that some reference is lost when the JNI_OnLoad function exits. I am converting all objects to global references but there must be still something getting lost.

Here is a minimal example of my setup. Any help will be highly appreciated.

Thanks!

JC

Native C:

JNIEnv   *jni_env = NULL;
jclass    jni_cls = NULL;
jobject   jni_obj = NULL;
jmethodID jni_myJavaFunc= NULL;

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    (void)reserved;

    jint ver = JNI_VERSION_1_6;

    if (vm == NULL)
        return JNI_ERR;

    if ((*vm)->GetEnv(vm, (void **)&jni_env, ver) != JNI_OK)
        return JNI_ERR;
        
    jni_cls = (jclass)(*jni_env)->NewGlobalRef(jni_env, (*jni_env)->FindClass(jni_env, "org/java/MyJavaClass"));

    if (jni_cls == NULL)
        return JNI_ERR;

    jmethodID init = (*jni_env)->GetMethodID(jni_env, jni_cls, "<init>", "()V");

    if (init == NULL)
        return JNI_ERR;

    jni_obj = (*jni_env)->NewGlobalRef(jni_env, (*jni_env)->NewObject(jni_env, jni_cls, init));

    if (jni_obj == NULL)
        return JNI_ERR;

    jni_myJavaFunc = (*jni_env)->GetMethodID(jni_env, jni_cls, "myJavaFunc", "()I");

    return ver;
}

int myNativeFunc()
{
    if (jni_myJavaFunc != NULL) {
    
        (*jni_env)->ExceptionClear(jni_env);

        int n = (*jni_env)->CallIntMethod(jni_env, jni_obj, jni_myJavaFunc);
        
        if (!(*jni_env)->ExceptionCheck(jni_env))
            return n;
        else
            return -1;
    }
}

Java class:

public class MyJavaClass extends Activity
{
    public MyJavaClass()
    {

    }
    
    public int myJavaFunc()
    {
        return 1;
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文