java JNI GetMethodID 不适用于内部类的构造函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25363027/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JNI GetMethodID not working for constructor of inner class
提问by user2340939
I have a class with a private subclass. I want to create an instance of that subclasss in a JNI wrapper and return it. I've googled and tried to make it work but with no success (methodID is null). Any suggestions?
我有一个带有私有子类的类。我想在 JNI 包装器中创建该子类的实例并返回它。我用谷歌搜索并试图让它工作但没有成功(methodID为空)。有什么建议?
JNIEXPORT jobject JNICALL Java_some_Class_some_Jni_Method(JNIEnv *env, jobject this) {
jclass cls = (*env)->FindClass(env, "someClass$someSubclass");
if (cls == NULL)
printf("jclass error.");
jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "()V"); // -> problem!
if (methodID == NULL)
printf("jmethodID error.");
jobject obj = (*env)->NewObject(env, cls, methodID);
if (obj == NULL)
printf("jobject error.");
return obj;
}
EDIT1: adding class definition:
EDIT1:添加类定义:
public class someClass
{
private class someSubclass {
public someSubclass() {
}
...
}
...
}
EDIT2: Ok I figured out you need parent class in the GetMethodID signature, so in my example: jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "(LsomeClass;)V");
EDIT2:好的,我发现你需要 GetMethodID 签名中的父类,所以在我的例子中: jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "(LsomeClass;)V");
But now I get EXCEPTION_ACCESS_VIOLATION with NewObject function.
但是现在我用 NewObject 函数得到了 EXCEPTION_ACCESS_VIOLATION。
EDIT3: I also needed to add calling class object/pointer to the NewObject function: jobject obj = (*env)->NewObject(env, cls, methodID, this);
EDIT3:我还需要向 NewObject 函数添加调用类对象/指针: jobject obj = (*env)->NewObject(env, cls, methodID, this);
Constructor of nested class is now called properly.
现在可以正确调用嵌套类的构造函数。
采纳答案by user2340939
You need parent class in the GetMethodID signature, so in my example:
jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "(LsomeClass;)V");
您需要 GetMethodID 签名中的父类,因此在我的示例中:
jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "(LsomeClass;)V");
And I also needed to add calling class object/pointer to the NewObject function:
jobject obj = (*env)->NewObject(env, cls, methodID, this);
而且我还需要向 NewObject 函数添加调用类对象/指针:
jobject obj = (*env)->NewObject(env, cls, methodID, this);
回答by Richard Chambers
I thought to provide a more involved answer to this question. The following is a simplified version of some experiments I am doing with JNI to learn how to use it. This example is more about exploring how to access objects and fields using JNI rather than to be a recommendation as to use.
我想为这个问题提供一个更复杂的答案。下面是我用JNI做的一些实验的简化版,以学习如何使用它。这个例子更多地是关于探索如何使用 JNI 访问对象和字段,而不是作为使用建议。
Also the Java source is slightly modified removing quite a bit of other source dealing with other JNI uses. However this should provide a starting place. There are best practices for JNI such as caching of field identifiers which are being ignored in this example. Here are some best practices using JNI from IBM.
此外,Java 源代码略有修改,删除了大量处理其他 JNI 用途的其他源代码。然而,这应该提供一个起点。JNI 有一些最佳实践,例如缓存在本示例中被忽略的字段标识符。以下是使用 IBM 的 JNI 的一些最佳实践。
In this example taken from that source the idea was to have a class, helloworld
, which contained an inner class, ExportedFuncs
, which would have various methods which acted as an interface to a set of native C functions exported from a dynamic link library (DLL). This inner class would in turn have its own inner class, ExportedData
, which would be a data only class.
在取自该来源的这个示例中,想法是有一个类,helloworld
,其中包含一个内部类,ExportedFuncs
,该类将具有各种方法,这些方法充当从动态链接库 (DLL) 导出的一组本机 C 函数的接口。这个内部类又会有它自己的内部类,ExportedData
,这将是一个只有数据的类。
When an ExportedFuncs
object was created, it would do a native call using JNI to obtain an instance of an ExportedData class.
当ExportedFuncs
创建对象时,它会使用JNI来获得ExportedData类的实例做一个本地电话。
Assume a simple example Java class with an encapsulated inner class. This example has an inner class that has an inner class.
假设有一个带有封装内部类的简单示例 Java 类。这个例子有一个具有内部类的内部类。
public class helloworld {
private class ExportedFuncs
{
// declare our private, data only class with some fields
private class ExportedData
{
int theInt;
String theString;
}
public native ExportedData getExportedData();
ExportedData theExportedData;
// constructor for the ExportedFuncs class which gets a copy of the data
ExportedFuncs()
{
theExportedData = getExportedData(); // get an object through native method
}
}
ExportedFuncs myExportedFuncs = new ExportedFuncs();
// .... other fields and methods of the helloworld class follows
}
The JNI native C function would look
JNI 原生 C 函数看起来像
JNIEXPORT jobject JNICALL Java_helloworld_00024ExportedFuncs_getExportedData (JNIEnv *env, jobject obj)
{
jfieldID fid = (*env)->GetFieldID(env, (*env)->GetObjectClass(env, obj), "theExportedData", "Lhelloworld$ExportedFuncs$ExportedData;");
jobject newObj = 0;
jclass cls = (*env)->FindClass(env, "Lhelloworld$ExportedFuncs$ExportedData;");
// Get the Method ID of the constructor for this inner class.
// There are two things to notice about this GetMethodID() function call.
// First, the constructor is requested by specifying the special string "<init>"
// Second, the signature of the constructor includes the enclosing class in the signature.
// Also there are no arguments for this constructor. if there were then they would need to be included between the parenthesis
// for example "(Lhelloworld$ExportedFuncs;I)V" for a single int arg.
jmethodID midInit = (*env)->GetMethodID(env, cls, "<init>", "(Lhelloworld$ExportedFuncs;)V");
if (NULL == midInit) return NULL;
// Call the class constructor to allocate a new instance. the default constructor has no arguments.
newObj = (*env)->NewObject(env, cls, midInit);
// now lets set some values in our new object and return it.
if (newObj) {
jfieldID fidAge = (*env)->GetFieldID (env, cls, "theInt", "I");
(*env)->SetIntField (env, newObj, fidAge, 127);
}
return newObj;
}
The function signature for the native JNI code was generated using the javah
utility on the helloworld
class. You may also find the output from the javap
utility helpful as well.
本机 JNI 代码的函数签名是使用类javah
上的实用程序生成的helloworld
。您可能还会发现该javap
实用程序的输出也很有帮助。
By the way I thought it interesting that the name of the native method of the inner class has the numeric field of five digits, 00024, which is the hex for the US dollar sign ($) in the ANSI/ASCII table. The US dollar sign is used for the separator for inner classes in a fully qualified name used in JNI functions such as GetFieldID()
.
顺便说一句,我觉得有趣的是,内部类的本地方法的名称有一个五位数的数字字段,00024,它是 ANSI/ASCII 表中美元符号 ($) 的十六进制。美元符号用于 JNI 函数(如GetFieldID()
.
I am not using packages in this contrived example so there is no package component to the native C function name. Ordinarily there would be. And a question I have is what are the limits of the function name length used with that that naming convention.
我没有在这个人为的例子中使用包,所以本地 C 函数名称没有包组件。正常情况下会有的。我的一个问题是,与该命名约定一起使用的函数名称长度的限制是什么。
Notice that both the GetFieldID()
and the FindClass()
functions use a fully qualified class name of "Lhelloworld$ExportedFuncs$ExportedData;"
which has the inner classes separated by the US dollar sign ($).
请注意,GetFieldID()
和FindClass()
函数都使用完全限定的类名,"Lhelloworld$ExportedFuncs$ExportedData;"
其内部类由美元符号 ($) 分隔。
The GetMethodID()
function must include the parent classes of any inner class. If the method being looked up was within the main class, helloworld
, then the call would look like:
该GetMethodID()
函数必须包含任何内部类的父类。如果要查找的方法在主类中helloworld
,则调用将如下所示:
jmethodID midInit = (*env)->GetMethodID(env, cls, "<init>", "()V");
However since we are wanting to construct an inner class of an inner class we need to specify the parent classes for the inner class we want to construct as in:
但是,由于我们要构造内部类的内部类,因此需要为要构造的内部类指定父类,如下所示:
jmethodID midInit = (*env)->GetMethodID(env, cls, "<init>", "(Lhelloworld$ExportedFuncs;)V");
One other point is that the constructor for the ExportedData
class is the default constructor which does not take any arguments. If there were arguments then those would need to be added to the method signature used in the GetMethodID()
function call. So if a constructor that took an int
was being used then the signature would look like "(Lhelloworld$ExportedFuncs;I)V"
.
另一点是ExportedData
该类的构造函数是不带任何参数的默认构造函数。如果有参数,则需要将这些参数添加到GetMethodID()
函数调用中使用的方法签名中。因此,如果使用了带 an 的构造函数,int
则签名将类似于"(Lhelloworld$ExportedFuncs;I)V"
.
回答by Bangyno
user2340939's answer help me to find the right way to construct an new object with integer argument of inner class. Here is my reward.
user2340939的回答帮助我找到了用内部类的整数参数构造新对象的正确方法。这是我的奖励。
JAVA
爪哇
package xxx.test_jni;
public class myNDK {
public myNDK() { Log.d("myNDK","myNDK constructor"); }
public myNDK(int a) { Log.d("myNDK","myNDK constructor(int)"); }
static {
System.loadLibrary("myJNI");
}
public class myObj {
int aaa;
public myObj(){
Log.d("myNDK","myObj()");
this.aaa = 333;
}
public myObj(int aaa){
Log.d("myNDK","myObj(int) " + aaa);
this.aaa = aaa;
}
public int getAaa() {
Log.d("myNDK","getAaa()");
return aaa;
}
}
public native myObj getmyObj1();
public native myObj getmyObj2();
}
CPP
CPP
JNIEXPORT jobject JNICALL Java_xxx_test_1jni_myNDK_getmyObj1
(JNIEnv *env, jobject thiz){
// Find inner class
jclass innerCls = env->FindClass("xxx/test_jni/myNDK$myObj");
if (innerCls == NULL) {
LOGI("%s, FindClass nullptr\n", __func__);
return NULL;
}
// Get Method ID myObj(), constructor
jmethodID cnstrctr1 = env->GetMethodID(innerCls, "<init>", "(Lxxx/test_jni/myNDK;)V");
if (cnstrctr == NULL) {
LOGI("%s, GetMethodID nullptr\n", __func__);
return NULL;
}
jobject obj1 = env->NewObject(innerCls, cnstrctr1, thiz);
if (obj1 == NULL) {
LOGI("%s, NewObject nullptr\n", __func__);
return NULL;
}
// Get Method ID myObj(int), constructor
jmethodID cnstrctr2 = env->GetMethodID(innerCls, "<init>", "(Lxxx/test_jni/myNDK;I)V");
if (cnstrctr2 == NULL) {
LOGI("%s, GetMethodID2 nullptr\n", __func__);
return NULL;
}
jint a = 5;
jobject obj2 = env->NewObject(innerCls, cnstrctr2, thiz, a);
if (obj2 == NULL) {
LOGI("%s, NewObject2 nullptr\n", __func__);
return NULL;
}
return obj2; // or obj1
}
To NewObject, NOTan inner class
对于 NewObject,不是内部类
JNIEXPORT jobject JNICALL Java_xxx_test_1jni_myNDK_getmyObj2
(JNIEnv *env, jobject thiz){
jclass cls = env->FindClass("xxx/test_jni/myNDK");
// Get Method ID myNDK(), constructor
jmethodID cnstrctr1 = env->GetMethodID(cls, "<init>", "()V");
jobject obj1 = env->NewObject(cls, cnstrctr1);
// Get Method ID myNDK(int), constructor
jmethodID cnstrctr2 = env->GetMethodID(cls, "<init>", "(I)V");
jint a = 1;
jobject obj2 = env->NewObject(cls, cnstrctr2, a);
return obj2; // or obj1
}
But I still want to know which document tells the NewObject API of inner class have to add parenter class?
但是我还是想知道哪个文件告诉内部类的NewObject API必须添加父类?