JNI GetMethodID не работает для конструктора внутреннего класса

У меня есть класс с частным подклассом. Я хочу создать экземпляр этого подкласса в оболочке 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: добавление определения класса:

public class someClass 
{ 
    private class someSubclass {    

        public someSubclass() {
        }
    ...
    }
...
}

EDIT2: Хорошо, я понял, что вам нужен родительский класс в подписи GetMethodID, поэтому в моем примере: jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "(LsomeClass;)V");

Но теперь я получаю EXCEPTION_ACCESS_VIOLATION с функцией NewObject.

EDIT3: мне также нужно было добавить вызывающий объект класса / указатель на функцию NewObject: jobject obj = (*env)->NewObject(env, cls, methodID, this);

Конструктор вложенного класса теперь вызывается правильно.

3 ответа

Решение

Вам нужен родительский класс в подписи GetMethodID, поэтому в моем примере:jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "(LsomeClass;)V");

И мне также нужно было добавить вызывающий объект класса / указатель на функцию NewObject:jobject obj = (*env)->NewObject(env, cls, methodID, this);

Я подумал дать более сложный ответ на этот вопрос. Ниже приведена упрощенная версия некоторых экспериментов, которые я провожу с JNI, чтобы узнать, как его использовать. Этот пример больше о том, как получить доступ к объектам и полям с помощью JNI, а не о том, как их использовать.

Кроме того, исходный код Java немного изменен, удаляя немало других источников, связанных с другими видами использования JNI. Однако это должно обеспечить отправную точку. Существуют лучшие практики для JNI, такие как кэширование идентификаторов полей, которые в этом примере игнорируются. Вот несколько лучших практик использования JNI от IBM.

В этом примере, взятом из этого источника, идея состояла в том, чтобы иметь класс, helloworld, который содержал внутренний класс, ExportedFuncs, который имел бы различные методы, которые действовали как интерфейс для набора собственных функций C, экспортируемых из библиотеки динамической компоновки (DLL). Этот внутренний класс, в свою очередь, будет иметь свой собственный внутренний класс, ExportedData, который будет класс только для данных.

Когда ExportedFuncs объект был создан, он будет выполнять собственный вызов с использованием JNI для получения экземпляра класса ExportedData.

Предположим простой пример 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
}

Родная функция C JNI будет выглядеть

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;
}

Сигнатура функции для собственного кода JNI была сгенерирована с использованием javah утилита на helloworld учебный класс. Вы также можете найти выход из javap полезность также полезна.

Кстати, мне показалось интересным, что имя нативного метода внутреннего класса имеет числовое поле из пяти цифр 00024, которое является шестнадцатеричным для знака доллара США ($) в таблице ANSI/ASCII. Знак доллара США используется для разделителя для внутренних классов в полностью определенном имени, используемом в функциях JNI, таких как GetFieldID(),

В этом надуманном примере я не использую пакеты, поэтому в имени функции родной Си нет компонента пакета. Обычно там будет. И у меня есть вопрос, каковы ограничения длины имени функции, используемой в соответствии с этим соглашением об именах.

Обратите внимание, что оба GetFieldID() и FindClass() функции используют полное имя класса "Lhelloworld$ExportedFuncs$ExportedData;" который имеет внутренние классы, разделенные знаком доллара США ($).

GetMethodID() Функция должна включать родительские классы любого внутреннего класса. Если искомый метод был в основном классе, helloworldто вызов будет выглядеть так:

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

Однако, поскольку мы хотим создать внутренний класс внутреннего класса, нам нужно указать родительские классы для внутреннего класса, который мы хотим построить, как в:

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

Еще один момент заключается в том, что конструктор для ExportedData class является конструктором по умолчанию, который не принимает никаких аргументов. Если бы были аргументы, их нужно было бы добавить к сигнатуре метода, используемой в GetMethodID() вызов функции. Так что, если конструктор, который взял int использовалась тогда подпись будет выглядеть "(Lhelloworld$ExportedFuncs;I)V",

user2340939 поможет мне найти правильный способ создания нового объекта с целочисленным аргументом внутреннего класса. Вот моя награда.


ДЖАВА

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
}

To 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
}

Но я все еще хочу знать, какой документ говорит, что API NewObject внутреннего класса должен добавить класс парентера?

Очень важно обратить внимание на ответ @user2340939:

/questions/4701154/jni-getmethodid-ne-rabotaet-dlya-konstruktora-vnutrennego-klassa/4701175#4701175

Я получил ошибки о слабых локальных ссылках, потому что Java думала, что моим первым аргументом был родительский класс.

Если вы можете, вы можете сделать внутренний класс статическим, и тогда вам не нужно помещать подпись родительского класса в конструктор, а также передавать родительский класс в конструкторе при создании объекта.

Другие вопросы по тегам