Cómo crear un objeto con JNI?


Necesito implementar algunas funciones en una aplicación Android usando NDK y, por lo tanto, JNI.

Aquí está el código C, con mis preocupaciones, que escribí:

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

Mis problemas están más o menos explicados dentro del código. Quizás también: ¿está bien el tipo de retorno de la función (jobject)?

Ahora el 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");
    }
}

Cuando intento ejecutar el código, no funciona.

Author: msrd0, 2011-08-31

5 answers

Dado que Point es una clase interna, la manera de obtenerla sería

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

La convención $ para las clases internas no está claramente documentada en las especificaciones autorizadas, pero está arraigada en tanto código de trabajo que es poco probable que cambie. Aún así, se sentiría algo más robusto si restringiera su código JNI para que funcione con clases de nivel superior.

Desea un constructor que tome dos ints como argumentos. La firma para eso es (II)V, así que:

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

Siguiente tiempo, incluya algún manejo de errores en su código, de modo que tendrá una pista de qué parte no funciona!

 72
Author: Henning Makholm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-31 16:43:53

La especificación es correcta, pero un poco engañosa en este caso. GetMethodID requiere un nombre de método y una firma de método. La especificación dice :

Para obtener el ID del método de un constructor, proporcione como el nombre del método y void (V) como el tipo de retorno.

Tenga en cuenta que dice tipo de retorno, no firma. Aunque void(V) se parece superficialmente a una firma, la especificación le dice que la firma debe especificar un void (es decir, V) devuelve el tipo.

La firma correcta para un constructor sin argumento es ()V. Si el constructor tiene argumentos, deben describirse entre paréntesis, como han señalado otros comentaristas.

 10
Author: pburka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-01 20:39:39

Algunos problemas con su código.

Primero, ¿por qué estás creando tu propia clase Point en lugar de usar Android proporcionado por la biblioteca?gráficos.El punto?

En segundo lugar, la especificación de clase para las clases anidadas es diferente - sería "com/example/ndktest/NDKTest Point Point". El anidamiento de clases es diferente de los paquetes.

Tercero, no creo que el JNI te permita crear instancias de clases anidadas no estáticas. Necesita pasar el puntero de la clase de anidamiento object' this en la creación del objeto - no hay tal argumento.

Finalmente, aunque he visto la guía para usar "void(V)" como una firma de método constructor, pero esto está fuera de línea con el resto de firmas de método; normalmente, un método con dos parámetros int y tipo de retorno void sería "(II)V".

Como nota al margen, me pareció mucho más limpio pasar tipos primitivos y matrices de tipos primitivos de NDK a Java. La creación/acceso de objetos es desordenada y difícil de depurar.

 4
Author: Seva Alekseyev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-31 16:57:22

En JNI siempre puede usar la herramienta javap para encontrar firmas de métodos. Simplemente ejecute javap -s com.example.ndktest.NDKTest y copie las firmas del método de la salida.

 1
Author: Christian Aberger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-22 05:45:39

Tres pasos deben crear el objeto Point con 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);
    ...
}
 0
Author: Bangyno,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-08 10:35:39