Как создать объект с JNI?
Мне нужно реализовать некоторые функции в Android-приложении, используя NDK и, следовательно, JNI.
Вот код C, с моими проблемами, который я написал:
#include <jni.h>
#include <stdio.h>
jobject
Java_com_example_ndktest_NDKTest_ImageRef(JNIEnv* env, jobject obj, jint width, jint height, jbyteArray myArray)
{
jint i;
jobject object;
jmethodID constructor;
jobject cls;
cls = (*env)->FindClass(env, "com/example/ndktest/NDKTest/Point");
//what should put as the second parameter? Is my try correct, according to what
//you can find in .java file? I used this documentation: http://download.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#wp16027
constructor = (*env)->GetMethodID(env, cls, "<init>", "void(V)");
//http://download.oracle.com/javase/6/docs/technotes/guides/jni/spec/functions.html#wp16660
//Again, is the last parameter ok?
object = (*env)->NewObject(env, cls, constructor, 5, 6);
//I want to assign "5" and "6" to point.x and point.y respectively.
return object;
}
Мои проблемы более или менее объясняются внутри кода. Возможно, также: является ли возвращаемый тип функции (jobject) нормально?
Теперь NDKTest.java:
package com.example.ndktest;
import android.app.Activity;
import android.widget.TextView;
import android.os.Bundle;
public class NDKTest extends Activity {
/** Called when the activity is first created. */
public native Point ImageRef(int width, int height, byte[] myArray);
public class Point
{
Point(int myx, int myy)
{
x = myx;
y = myy;
}
int x;
int y;
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
byte[] anArray = new byte[3];
for (byte i = 0; i < 3; i++)
anArray[i] = i;
Point point = ImageRef(2, 3, anArray);
tv.setText(String.valueOf(point.x));
setContentView(tv);
}
static
{
System.loadLibrary("test");
}
}
Когда я пытаюсь запустить код, он не работает.
Ответы
Ответ 1
Так как Point
является внутренним классом, способ получить его будет
jclass cls = (*env)->FindClass(env, "com/example/ndktest/NDKTest$Point");
Конгресс $
для внутренних классов на самом деле не задокументирован в авторитетных спецификациях, но укоренился в таком рабочем коде, который вряд ли изменится. Тем не менее, он будет чувствовать себя более уверенно, если вы ограничили свой JNI-код работой с классами верхнего уровня.
Вам нужен конструктор, который принимает два ints в качестве аргументов. Подпись для этого (II)V
, поэтому:
constructor = (*env)->GetMethodID(env, cls, "<init>", "(II)V");
В следующий раз включите некоторую обработку ошибок в свой код, чтобы у вас была подсказка, какая часть ее не работает!
Ответ 2
Спецификация верна, но в этом случае она немного вводит в заблуждение. GetMethodID
требуется имя метода и подпись метода. В спецификации указано:
Чтобы получить идентификатор метода конструктора, поставьте <init> как имя метода и void (V) в качестве возвращаемого типа.
Обратите внимание, что он говорит тип возврата, а не подпись. Хотя void(V)
внешне внешне похож на подпись, спецификация сообщает вам, что подпись должна указывать тип возврата void (т.е. V
).
Правильной сигнатурой для конструктора без аргументов является ()V
. Если у конструктора есть аргументы, они должны быть описаны между круглыми скобками, как отмечали другие комментаторы.
Ответ 3
Некоторые проблемы с вашим кодом.
Во-первых, почему вы создаете свой собственный класс Point вместо использования предоставленного библиотекой android.graphics.Point?
Во-вторых, спецификация класса для вложенных классов отличается - это будет "com/example/ndktest/NDKTest $Point". Вложенность классов отличается от пакетов.
В-третьих, я не думаю, что JNI позволяет создавать экземпляры нестатических вложенных классов. Вам нужно передать указатель объекта-указателя класса "this
при создании объекта - нет такого аргумента.
Наконец, хотя я видел руководство использовать "void (V)" как подпись метода конструктора, но это не соответствует остальным сигнатурам методов; обычно, метод с двумя параметрами int и типом возврата void будет "(II) V".
В качестве побочного примечания я счел гораздо более чистым передавать примитивные типы и массивы примитивов, набранных с NDK на Java. Создание/доступ к объектам беспорядочно и сложно отлаживать.
Ответ 4
В JNI вы всегда можете использовать инструмент javap
для поиска сигнатур методов. Просто запустите javap -s com.example.ndktest.NDKTest
и скопируйте сигнатуры метода из вывода.
Ответ 5
Три шага должны создать объект Point с JNI:
jobject
Java_com_example_ndktest_NDKTest_ImageRef(JNIEnv* env, jobject obj, jint width, jint height, jbyteArray myArray)
{
...
jclass cls = (*env)->FindClass(env, "com/example/ndktest/NDKTest$Point");
jmethodID constructor = (*env)->GetMethodID(env, cls, "<init>", "void(V)");
jobject object = (*env)->NewObject(env, cls, constructor, obj, 5, 6);
...
}