使用 JNI 在 C 中访问 Java 对象中的 Java 对象

Posted

技术标签:

【中文标题】使用 JNI 在 C 中访问 Java 对象中的 Java 对象【英文标题】:Accessing a Java object in a Java object in C using JNI 【发布时间】:2012-08-03 22:04:53 【问题描述】:

我对 JNI 比较陌生,并且已经掌握了使用 JNI 在 Java 对象中处理整数和数组的基础知识。现在我正在尝试修改/访问 Java 对象中的 Java 对象。

我一直在互联网和 Stack Overflow 上进行搜索,但还没有找到如何做到这一点。

这是示例。

在 Java 中:

public class ObjectOne

    private byte[] buff;
    ...
    ...


public class ObjectTwo

    private ObjectOne obj;
    ...
    ...

在 JNI 中,如何通过 ObjectTwo 从 ObjectOne 访问“buff”?我尝试过这样的事情......

JNIEXPORT void JNICALL Java_accessBuffThroughObjectTwo(JNIEnv *env, jobject obj, jobject objectTwo)

    jclass clazz;
    jclass bufferClazz;
    jobject bufferJObject;

    clazz = (*env)->GetObjectClass(env, objectTwo);
    fid = (*env)->GetFieldID(env, clazz, "obj", "Ljava/lang/Object;");
    bufferJObject = (*env)->GetObjectField(env, javascsicommand, fid);
    bufferClazz = (*env)->GetObjectClass(env, bufferJObject);  <-- Fails here for Access Violation
    fid = (*env)->GetFieldID(env, bufferClazz, "buff", "[B");

对我做错了什么有帮助吗?

【问题讨论】:

第一步是检查每个 JNI 调用是否失败。二:javascsicommand是什么? 我相信您将 obj 实例的错误值传递给 GetObjectField。查看正确的描述符:jobject GetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID); 也许您的意思是传递objectTwo 而不是javascsicommand 您无法访问其他对象的私有字段。您需要提供getter,更改访问权限关键字,或打开访问运行时。 @qrtt1 是的,您可以使用 JNI 访问私有字段。 【参考方案1】:

在尝试您的代码时,您可以轻松地添加一些这样的断言:

JNIEXPORT void JNICALL Java_accessBuffThroughObjectTwo(JNIEnv *env, jobject obj,  jobject objectTwo) 
    jclass clazz;
    jclass bufferClazz;
    jobject bufferJObject;
    jfieldID fid;

    clazz = (*env)->GetObjectClass(env, objectTwo);
    assert(clazz != NULL);
    fid = (*env)->GetFieldID(env, clazz, "obj", "Ljava/lang/Object;");
    assert(fid != NULL);
    bufferJObject = (*env)->GetObjectField(env, javascsicommand, fid);
    assert(bufferJObject != NULL);
    bufferClazz = (*env)->GetObjectClass(env, bufferJObject);
    assert(bufferClazz != NULL);
    fid = (*env)->GetFieldID(env, bufferClazz, "buff", "[B");
    assert(fid != NULL);

这样做你会首先看到第一个fid 将为NULL。这是因为ObjectTwo 类没有java.lang.Object 类型的任何字段。您应该将行更改为如下所示(但添加正确的包而不是 com/package):

fid = (*env)->GetFieldID(env, clazz, "obj", "Lcom/package/ObjectOne;");

再次运行会发现fid不再为null,断言会通过。

正如其他人所建议的那样,我认为javascsicommand 应该是objectTwo

现在断言失败的下一个地方是bufferJObject。这是因为该字段存在但对象为 NULL,如果您检查您的 java 代码,您会注意到 obj 字段从未实例化并且是 null

把你的java代码改成这样:

public class ObjectTwo

    private ObjectOne obj = new ObjectOne();
    ...
    ...

您现在将传递断言,甚至传递所有其他断言。

总而言之,您正在访问一个 null 对象并尝试对其调用反射:

bufferClazz = (*env)->GetObjectClass(env, bufferJObject); <-- The bufferJObject was NULL

【讨论】:

感谢您的详细回复。我肯定会利用断言并检查 NULL。当您谈论访问空对象时,我认为您一针见血。我认为这就是给我访问冲突的原因 @user1575243 如果您认为我的回答有用,请将答案标记为已接受! @user1575243 如果像我这样的人花一些时间来设置编码环境,开始调试会话并尝试解决其他人的问题,那么您至少可以期望他们接受他们认为有用的答案... 对此感到抱歉。我不知道接受答案是如此有价值。已接受答案。

以上是关于使用 JNI 在 C 中访问 Java 对象中的 Java 对象的主要内容,如果未能解决你的问题,请参考以下文章

JNI/NDK开发指南——C/C++访问Java实例变量和静态变量

JNI/NDK开发指南——C/C++访问Java实例方法和静态方法

JNI/NDK开发指南——C/C++访问Java实例方法和静态方法

IntelliJ IDEA平台下JNI编程—本地C代码访问JAVA对象

JNI - C++/Java 中的侦听器 - 是不是可以在 C++ 中实例化 Java 对象并将它们用作参数

IntelliJ IDEA平台下JNI编程—本地C代码创建Java对象及引用