Как создать объект с JNI? - PullRequest
       24

Как создать объект с JNI?

50 голосов
/ 31 августа 2011

Мне нужно внедрить некоторые функции в приложение 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");
    }
}

Когда я пытаюсь запустить код, он не работает.

Ответы [ 5 ]

78 голосов
/ 31 августа 2011

Поскольку Point является внутренним классом, способ получить его будет

jclass cls = (*env)->FindClass(env, "com/example/ndktest/NDKTest$Point");

Соглашение $ для внутренних классов не совсем четко задокументировано в авторитетных спецификациях, но укоренилось в таком большом количестве рабочего кода, что вряд ли изменится. Тем не менее, было бы чувствовать себя несколько более устойчивым, если бы вы ограничивали свой код JNI для работы с классами верхнего уровня.

Вам нужен конструктор, который принимает два аргумента в качестве аргументов. Подпись для этого (II)V, поэтому:

constructor = (*env)->GetMethodID(env, cls, "<init>", "(II)V");

В следующий раз включите в свой код некоторую обработку ошибок, чтобы вы могли понять, какая часть этого не работает!

13 голосов
/ 08 января 2014

Спецификация верна, но в этом случае немного вводит в заблуждение. GetMethodID требует имя метода и подпись метода. Спецификация гласит: :

Чтобы получить идентификатор метода конструктора, укажите в качестве имени метода и void (V) в качестве возвращаемого типа.

Обратите внимание, что в нем указано тип возврата , а не подпись . Хотя void(V) внешне похож на подпись, спецификация говорит вам, что подпись должна указывать тип возврата void (то есть V).

Правильная подпись для конструктора без аргументов: ()V. Если конструктор имеет аргументы, они должны быть описаны в скобках, как отмечали другие комментаторы.

6 голосов
/ 31 августа 2011

Некоторые проблемы с вашим кодом.

Во-первых, почему вы создаете свой собственный класс Point, а не используете предоставляемый библиотекой android.graphics.Point?

Во-вторых, спецификация класса для вложенногоклассы разные - это будет "com / example / ndktest / NDKTest $ Point".Вложение классов отличается от пакетов.

В-третьих, я не думаю, что JNI позволяет создавать экземпляры нестатических вложенных классов.Вам нужно передать указатель вложенного класса объекта 'this при создании объекта - такого аргумента нет.

Наконец, пока я видел руководство по использованию "void (V)" в качестве сигнатуры метода конструктора, но это не соответствует остальным сигнатурам метода;обычно, метод с двумя параметрами int и типом возврата void будет "(II) V".

В качестве примечания, я обнаружил, что гораздо проще передавать примитивные типы и массивы примитивных типов, типизированных из NDK, в Java,Создание / доступ к объектам грязный и сложный для отладки.

4 голосов
/ 22 марта 2017

В JNI вы всегда можете использовать инструмент javap для поиска сигнатур методов. Просто запустите javap -s com.example.ndktest.NDKTest и скопируйте сигнатуры метода из вывода.

1 голос
/ 08 сентября 2016

Три шага должны создать объект 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);
    ...
}
...