wake-up-neo.com

Wie erstelle ich ein Objekt mit JNI?

Ich muss einige Funktionen in eine Android-Anwendung mit NDK und damit JNI implementieren.

Hier ist der C-Code mit meinen Bedenken, den ich geschrieben habe:

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

Meine Probleme werden mehr oder weniger im Code erklärt. Vielleicht auch: Ist der Rückgabetyp der Funktion (jobject) in Ordnung?

Nun das 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");
    }
}

Wenn ich versuche, den Code auszuführen, funktioniert er nicht.

47
pmichna

Da Point eine innere Klasse ist, wäre der Weg, es zu bekommen

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

Die $ -Konvention für innere Klassen ist in den maßgeblichen Spezifikationen nicht wirklich klar dokumentiert, ist jedoch in so viel Arbeitscode verankert, dass es unwahrscheinlich ist, dass sich daran etwas ändert. Trotzdem wäre es feel etwas robuster, wenn Sie Ihren JNI-Code auf die Arbeit mit Klassen der obersten Ebene beschränken würden.

Sie möchten einen Konstruktor, der zwei Ints als Argumente verwendet. Die Signatur dafür ist (II)V, also:

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

Schließen Sie das nächste Mal eine Fehlerbehandlung in Ihren Code ein, sodass Sie eine Ahnung haben, welcher Teil davon nicht funktioniert!

79
Henning Makholm

Die Spezifikation ist korrekt, aber in diesem Fall etwas irreführend. GetMethodID erfordert einen Methodennamen und eine Methodensignatur. Die Spezifikation sagt :

Um die Methoden-ID eines Konstruktors zu erhalten, geben Sie als Namen der Methode <init> und als Rückgabetyp void (V) an.

Beachten Sie, dass return type und nicht signature angezeigt wird. Obwohl void(V) einer Signatur oberflächlich ähnlich aussieht, teilt Ihnen die Spezifikation mit, dass die Signatur einen ungültigen (dh V) Rückgabetyp angeben muss.

Die korrekte Signatur für einen Konstruktor ohne Argument ist ()V. Wenn der Konstruktor Argumente hat, sollten diese zwischen den Klammern beschrieben werden, wie andere Kommentatoren dies bemerkt haben.

12
pburka

Einige Probleme mit Ihrem Code.

Erstens, warum erstellen Sie Ihre eigene Point-Klasse, anstatt die von Android.graphics.Point aus der Bibliothek bereitgestellte zu verwenden?

Zweitens ist die Klassenspezifikation für verschachtelte Klassen unterschiedlich - es wäre "com/example/ndktest/NDKTest $ Point". Klassenschachtelung unterscheidet sich von Paketen.

Drittens glaube ich nicht, dass Sie mit JNI Instanzen nicht statischer verschachtelter Klassen erstellen können. Sie müssen bei der Objekterstellung den Schachtelklassenobjekt 'this' übergeben. Es gibt kein solches Argument.

Schließlich habe ich zwar die Anleitung zur Verwendung von "void (V)" als Methodensignatur des Konstruktors gesehen, aber diese stimmt nicht mit den übrigen Methodensignaturen überein. Normalerweise wäre eine Methode mit zwei int-Parametern und einem ungültigen Rückgabetyp "(II) V".

Als Randbemerkung fand ich es viel sauberer, primitive Typen und Arrays von primitivem Typ von NDK an Java zu übergeben. Objekterstellung/-zugriff ist unordentlich und schwer zu debuggen.

6
Seva Alekseyev

In JNI können Sie das Werkzeug javap zum Suchen von Methodensignaturen verwenden. Führen Sie einfach javap -s com.example.ndktest.NDKTest aus und kopieren Sie die Methodensignaturen aus der Ausgabe. 

4

In drei Schritten sollte das Point-Objekt mit JNI erstellt werden:

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