web-dev-qa-db-fra.com

Comment créer un objet avec JNI?

J'ai besoin d'implémenter certaines fonctions dans une application Android utilisant NDK et donc JNI.

Voici le code C, avec mes préoccupations, que j'ai écrit:

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

Mes problèmes sont plus ou moins expliqués dans le code. Peut-être aussi: le type de retour de la fonction (jobject) est-il correct?

Maintenant le 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");
    }
}

Lorsque j'essaie d'exécuter le code, cela ne fonctionne pas.

47
pmichna

Puisque Point est une classe intérieure, le moyen de l'obtenir serait

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

La convention $ pour les classes internes n'est pas vraiment clairement documentée dans les spécifications faisant autorité, mais elle est enchâssée dans un code tellement fonctionnel qu'il est peu probable que cela change. Cependant, il serait sentir un peu plus robuste si vous limitiez votre code JNI à fonctionner avec des classes de niveau supérieur.

Vous voulez un constructeur qui prend deux entiers comme arguments. La signature pour cela est (II)V, donc:

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

La prochaine fois, incluez des erreurs dans votre code pour que vous sachiez quelle partie ne fonctionne pas!

76
Henning Makholm

La spécification est correcte, mais un peu trompeuse dans ce cas. GetMethodID nécessite un nom de méthode et une signature de méthode. La spécification dit :

Pour obtenir l'ID de méthode d'un constructeur, entrez <init> comme nom de méthode et void (V) comme type de retour.

Notez qu'il est indiqué type de retour, pas signature. Bien que void(V) ressemble en apparence à une signature, la spécification vous indique que la signature doit spécifier un type de retour vide (c'est-à-dire V).

La signature correcte pour un constructeur sans argument est ()V. Si le constructeur a des arguments, ceux-ci doivent être décrits entre parenthèses, comme d'autres commentateurs l'ont noté.

12
pburka

Quelques problèmes avec votre code.

Tout d'abord, pourquoi créez-vous votre propre classe de points plutôt que d'utiliser Android.graphics.Point fourni par la bibliothèque?

Deuxièmement, la spécification de classe pour les classes imbriquées est différente - ce serait "com/exemple/ndktest/NDKTest $ Point". L'imbrication de classe est différente des packages.

Troisièmement, je ne pense pas que JNI vous permet de créer des instances de classes imbriquées non statiques. Vous devez passer le pointeur this de l'objet de classe imbriquée lors de la création de l'objet. Cet argument n'existe pas.

Enfin, alors que j’ai vu les instructions pour utiliser "void (V)" comme signature de méthode constructeur, cela n’est pas conforme au reste des signatures de méthode; normalement, une méthode avec deux paramètres int et un type de retour vide serait "(II) V".

En remarque, je trouvais beaucoup plus simple de passer des types primitifs et des tableaux de primitives typées de NDK à Java. La création/l’accès aux objets est compliqué et difficile à déboguer.

6
Seva Alekseyev

Dans JNI, vous pouvez toujours utiliser l'outil javap pour rechercher des signatures de méthode. Il suffit d'exécuter javap -s com.example.ndktest.NDKTest et de copier les signatures de la méthode à partir de la sortie. 

4
Christian Aberger

Trois étapes doivent créer l'objet Point avec 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);
    ...
}
1
Bangyno