Appel d'une méthode Java à partir de C ++ sous Android

91

J'essaie d'obtenir un simple appel de méthode Java à partir de C ++ tandis que Java appelle la méthode native. Voici le code Java:

public class MainActivity extends Activity {
    private static String LIB_NAME = "name";

    static {
        System.loadLibrary(LIB_NAME);
    }

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        TextView tv = (TextView) findViewById(R.id.textview);
        tv.setText(this.getJniString());
    }

    public void messageMe(String text) {
        System.out.println(text);
    }

    public native String getJniString();
}

J'essaie d'appeler une messageMeméthode à partir du code natif dans le processus d' getJniString*appel de méthode de Java à natif.

native.cpp:

#include <string.h>
#include <stdio.h>
#include <jni.h>

jstring Java_the_package_MainActivity_getJniString( JNIEnv* env, jobject obj, jint depth ){

//    JavaVM *vm;
//    JNIEnv *env;
//    JavaVMInitArgs vm_args;
//    vm_args.version = JNI_VERSION_1_2;
//    vm_args.nOptions = 0;
//    vm_args.ignoreUnrecognized = 1;
//
//    // Construct a VM
//    jint res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);

    // Construct a String
    jstring jstr = env->NewStringUTF("This string comes from JNI");
    // First get the class that contains the method you need to call
    jclass clazz = env->FindClass("the/package/MainActivity");
    // Get the method that you want to call
    jmethodID messageMe = env->GetMethodID(clazz, "messageMe", "(Ljava/lang/String;)V");
    // Call the method on the object
    jobject result = env->CallObjectMethod(jstr, messageMe);
    // Get a C-style string
    const char* str = env->GetStringUTFChars((jstring) result, NULL);
    printf("%s\n", str);
        // Clean up
    env->ReleaseStringUTFChars(jstr, str);

//    // Shutdown the VM.
//    vm->DestroyJavaVM();

    return env->NewStringUTF("Hello from JNI!");
}

Après que l'application de compilation propre s'arrête avec le message suivant:

ERROR/AndroidRuntime(742): FATAL EXCEPTION: main
        java.lang.NoSuchMethodError: messageMe
        at *.android.t3d.MainActivity.getJniString(Native Method)
        at *.android.t3d.MainActivity.onCreate(MainActivity.java:22)

Apparemment, cela signifie que le nom de la méthode est faux, mais cela me semble correct.

Denys S.
la source
21
Veuillez publier votre solution sous forme de réponse ordinaire pour rendre votre question et la solution plus faciles à lire et donc plus utiles à la communauté. Vous pouvez également collaborer avec d'autres personnes qui ont déjà répondu pour compléter leurs réponses.
misiu_mp
@Denys: J'ai suivi votre codage, mais j'obtiens cette erreur: java.lang.UnsatisfiedLinkError: getJniString. Pouvez-vous m'aider à corriger cette erreur?
Huy Tower
@AlexTran, c'était il y a longtemps mais à en juger par l'erreur, vous avez probablement mal orthographié ou n'avez pas lié la getJniStringméthode en java ou en c. Assurez-vous de bien lier le code c à java probablement par importation système (srsly ne me souviens pas de tout cela maintenant: P)
Denys S.
1
Comment cela appelle-t-il une méthode java à partir de c? C'est manifestement la onCreateméthode de Java qui appelle votre C. natif
John
J'obtiens l' opérande de base de '->' a un type sans pointeur 'JNIEnv lorsqu'il est exécuté avec la variable d'environnement (env). Et si on voulait se passer de la variable env *, comme le rappel de JNI vers la couche Java! Toute suggestion!
CoDe du

Réponses:

45

S'il s'agit d'une méthode objet, vous devez transmettre l'objet à CallObjectMethod:

jobject result = env->CallObjectMethod(obj, messageMe, jstr);

Ce que vous faisiez était l'équivalent de jstr.messageMe().

Puisque votre est une méthode void, vous devez appeler:

env->CallVoidMethod(obj, messageMe, jstr);

Si vous souhaitez retourner un résultat, vous devez changer votre signature JNI (cela ()Vsignifie une méthode de voidtype de retour) ainsi que le type de retour dans votre code Java.

Matthew Willis
la source
S'il vous plaît, guidez-moi sur la façon de faire cela, à cause de mon PS :)
Denys S.
J'obtiens le même résultat avec ce que vous suggérez.
Denys S.
1
il existe en fait un CallVoidMethod, CallObjectMethod, etc. chacun avec un type de retour différent. Puisque votre méthode messageMe est (Ljava / lang / String;) V, vous devez utiliser CallVoidMethod.
Matthew Willis
2
notez que l'erreur que vous obtenez indique probablement que votre méthode native Java (dans votre code Java) n'est probablement pas de type de retour void et n'est donc pas trouvée par GetMethodID
Matthew Willis
10

Solution publiée par Denys S. dans le post de question:

Je l'ai assez gâché avec la conversion de C en C ++ (essentiellement envdes éléments variables), mais je l'ai fait fonctionner avec le code suivant pour C ++:

#include <string.h>
#include <stdio.h>
#include <jni.h>

jstring Java_the_package_MainActivity_getJniString( JNIEnv* env, jobject obj){

    jstring jstr = (*env)->NewStringUTF(env, "This comes from jni.");
    jclass clazz = (*env)->FindClass(env, "com/inceptix/android/t3d/MainActivity");
    jmethodID messageMe = (*env)->GetMethodID(env, clazz, "messageMe", "(Ljava/lang/String;)Ljava/lang/String;");
    jobject result = (*env)->CallObjectMethod(env, obj, messageMe, jstr);

    const char* str = (*env)->GetStringUTFChars(env,(jstring) result, NULL); // should be released but what a heck, it's a tutorial :)
    printf("%s\n", str);

    return (*env)->NewStringUTF(env, str);
}

Et le code suivant pour les méthodes java:

    public class MainActivity extends Activity {
    private static String LIB_NAME = "thelib";

    static {
        System.loadLibrary(LIB_NAME);
    }

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        TextView tv = (TextView) findViewById(R.id.textview);
        tv.setText(this.getJniString());
    }

    // please, let me live even though I used this dark programming technique
    public String messageMe(String text) {
        System.out.println(text);
        return text;
    }

    public native String getJniString();
}
BartoszKP
la source
Les nativeméthodes doivent-elles être statiques?
IgorGanapolsky