JNI GetMethodID not working for constructor of inner class

I have a class with a private subclass. I want to instantiate these subclasses in a JNI wrapper and return it. I googled and tried to get it to work, but without success (methodID is null). Any suggestions?

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 a class definition:

 public class someClass { private class someSubclass { public someSubclass() { } ... } ... } 

EDIT2: Good. I realized that you need the parent class in the GetMethodID signature, so in my example: jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "(LsomeClass;)V");

But now I get EXCEPTION_ACCESS_VIOLATION using the NewObject function.

EDIT3: I also needed to add the object / pointer of the calling class to the NewObject function: jobject obj = (*env)->NewObject(env, cls, methodID, this);

The constructor of the nested class is now called correctly.

+5
source share
3 answers

You need the parent class in the GetMethodID signature, so in my example: jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "(LsomeClass;)V");

And I also need to add an object / pointer to the call class to the NewObject function: jobject obj = (*env)->NewObject(env, cls, methodID, this);

+2
source

I thought about giving a more answer to this question. The following is a simplified version of some of the experiments I'm doing with JNI to find out how to use it. This example focuses more on how to access objects and fields using JNI, rather than usage guidelines.

Also, the Java source is slightly modified, removing quite a lot of another source using other JNI applications. However, this should be the starting point. There are best practices for the JNI, such as caching field identifiers, which are ignored in this example. Here are some guidelines for using JNI from IBM .

In this example, taken from this source, the idea was to have a helloworld class that contained an internal ExportedFuncs class that had various methods that acted as an interface to a set of original C functions exported from a dynamic link library (DLL) . This inner class, in turn, has its own inner class, ExportedData , which will only be a data class.

When the ExportedFuncs object was created, it will make its own call using the JNI to get an instance of the ExportedData class.

Suppose a simple example of a Java class with an encapsulated inner class. This example has an inner class that has an inner class.

 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 } 

C JNI native function will look

 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 JNI native code was generated using the javah utility in the helloworld class. You can also find useful information from the javap utility.

By the way, it seemed interesting to me that the name of the native method of the inner class has a five-digit number field 00024, which is the sixth for the US dollar sign ($) in the ANSI / ASCII table. The US dollar sign is used as a separator for inner classes with the full name used in JNI functions, for example GetFieldID() .

I do not use packages in this far-fetched example, so there is no package component for function name C. Normally this would be. And I have a question, what are the function name length limits used with this naming convention.

Note that the GetFieldID() and FindClass() functions use the fully qualified class name "Lhelloworld$ExportedFuncs$ExportedData;" whose inner classes are separated by a dollar sign ($).

The GetMethodID() function must include the parent classes of any inner class. If the method in question is in the main class, helloworld , then the call will look like this:

 jmethodID midInit = (*env)->GetMethodID(env, cls, "<init>", "()V"); 

However, since we want to build the inner class of the inner class, we need to specify the parent classes for the inner class that we want to build, as in:

 jmethodID midInit = (*env)->GetMethodID(env, cls, "<init>", "(Lhelloworld$ExportedFuncs;)V"); 

Another point is that the constructor for the ExportedData class is the default constructor that takes no arguments. If there were arguments, they would need to be added to the method signature used in the call to the GetMethodID() function. Therefore, if you used a constructor that accepted an int , then the signature would look like "(Lhelloworld$ExportedFuncs;I)V" .

+3
source

user2340939 answer help me find the right way to build a new object with an integer class argument. Here is my reward.


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

 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 } 

In NewObject, NOT an inner class

 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 inner class NewObject API to add a parter class?

+1
source

Source: https://habr.com/ru/post/1200585/


All Articles