web-dev-qa-db-fra.com

Appeler une méthode Java à partir de c ++ dans Android

J'essaie d'obtenir un simple Java de C++ tandis que Java appelle une 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 la méthode messageMe à partir du code natif dans le processus de getJniString* appel de méthode de Java vers 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 la compilation, l'application s'interrompt 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.

88
Denys S.

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

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

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

Puisque votre méthode est vide, vous devriez appeler:

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

Si vous voulez renvoyer un résultat, vous devez changer votre signature JNI (le ()V Signifie une méthode de void type de retour) ainsi que le type de retour dans votre Java code.

44
Matthew Willis

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

Je me suis vraiment trompé avec la conversion de c à c ++ (en gros, env variable), mais je l'ai obtenu 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();
}
8
BartoszKP