使用 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对象