2011-08-31 17 views
40

Ho bisogno di implementare alcune funzioni in un'applicazione Android utilizzando NDK e quindi JNI.Come creare un oggetto con JNI?

Ecco il codice C, con le mie preoccupazioni, che ho scritto:

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

I miei problemi sono più o meno spiegate all'interno del codice. Forse anche: è il tipo di ritorno della funzione (jobject) ok?

Ora la 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"); 
    } 
} 

Quando provo a eseguire il codice, non funziona.

+3

Si prega di spiegare "non funziona". –

+1

Anche se probabilmente intendevi "rispettivamente", penso sia importante trattare i tuoi oggetti con rispetto. :) – quasimodo

+0

@quasimodo Hai ragione. :) Ho modificato l'errore, grazie. – pmichna

risposta

67

Dal Point è una classe interna, il modo per farlo sarebbe

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

La convenzione $ per le classi interne non è realmente chiaramente documentato nelle specifiche autorevoli, ma è radicata nel codice così tanto lavoro che è improbabile che cambi. Eppure, sarebbe sentire un po 'più robusto se limitato il codice JNI per lavorare con classi di primo livello.

Si desidera un costruttore che accetta due argomenti come argomenti. La firma di ciò è (II)V, quindi:

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

La prossima volta, includono alcuni la gestione degli errori nel codice, in modo che avrete un indizio che parte di esso non funziona!

+1

Nota: questa risposta non funzionerà. Vedere i commenti nella risposta di Seva sotto per il perché (gli argomenti del costruttore devono includere la classe esterna) – Colin

+0

@Colin: Sì, ho trascurato il fatto che 'Point' non è statico. –

4

Alcuni problemi con il codice.

In primo luogo, perché si sta creando la propria classe Point anziché utilizzare android.graphics.Point fornito dalla libreria?

In secondo luogo, la specifica della classe per le classi annidate è diversa - sarebbe "com/example/ndktest/NDKTest $ Point". La nidificazione delle classi è diversa dai pacchetti.

In terzo luogo, non penso che JNI ti consenta di creare istanze di classi annidate non statiche. È necessario passare l'oggetto di classe di nidificazione' this puntatore creazione di un oggetto - non c'è tale argomento.

Infine, mentre ho visto la guida per usare "void (V)" come firma del metodo del costruttore, ma questo è fuori linea con il resto delle firme dei metodi; normalmente, un metodo con due parametri int e tipo restituito vuoto sarebbe "(II) V".

Come nota a margine, ho trovato molto più pulito passare tipi primitivi e matrici di primitive digitate da NDK a Java. La creazione/l'accesso agli oggetti è complicato e difficile da eseguire il debug.

+0

@Hennig Makholm Ho creato la mia propria classe Point solo per esempio. Potrebbe avere come _foo_. Ho cambiato la mia classe di essere di alto livello, ha cambiato "vuoto (V) per '(II) V' ed ora funziona – pmichna

+2

La documentazione afferma:." Questo ID deve essere ottenute chiamando GetMethodID() con come il nome del metodo e void (V) come tipo di ritorno. "Anch'io ero confuso da questo quando l'ho letto per la prima volta, pensavo significasse" usare la firma "void (V)" ma in realtà significava "usare il codice tipo V" (per nulla) quando costruisci la firma ". Sicuramente 'void (V)' sembra una firma strana, ma allora '" "' è un modo strano per specificare un costruttore. Quando tutto è magico comunque, le cose confuse sono davvero confuse! http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html – steveha

+9

È possibile, infatti, creare istanze di classi nidificate nonstatic, il metodo è solo un po 'non ovvio: tutti i costruttori per la classe nidificata hanno un primo parametro implicito del tipo della classe esterna. Così, per l'esempio precedente, con la classe Point non statica, la firma del costruttore sarebbe ' "", "(lcom/es/ndktest/NDKTest; II) V"'. Puoi vederlo eseguendo 'javap -s -p com.example.ndktest.NDKTest $ Point' dalla tua directory classes dopo una compilazione. – benkc

9

La specifica è corretta, ma un po 'fuorviante, in questo caso. GetMethodID richiede un nome di metodo e una firma del metodo. Il specification says:

Per ottenere l'ID metodo di un costruttore, alimentazione <init> come il nome del metodo e vuoto (V) come tipo di ritorno.

Nota che si dice tipo di ritorno, non firma. Anche se void(V) guarda superficialmente simile ad una firma, la specifica che si sta dicendo che la firma deve specificare un vuoto (cioè, V) tipo di ritorno.

La firma corretta per un costruttore senza argomenti è ()V. Se il costruttore ha argomenti, dovrebbero essere descritti tra parentesi, come hanno notato altri commentatori.

0

tre fasi dovrebbe creare l'oggetto 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); 
    ... 
} 
+1

Ciò è utile in quanto mostra un esempio di codice succinto. Tuttavia, il codice non funzionerà, perché la firma del metodo è errata. Dovrebbe essere "" (II) V "', come elencato in altre risposte. – ephemer

1

In JNI è sempre possibile utilizzare lo strumento per la ricerca di javap metodo firme. Basta eseguire javap -s com.example.ndktest.NDKTest e copiare le firme del metodo dall'output.

Problemi correlati