Aufrufen einer Java-Methode aus c ++ in Android

90

Ich versuche, einen einfachen Java-Methodenaufruf von C ++ zu erhalten, während Java die native Methode aufruft. Hier ist der Java-Code:

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();
}

Ich versuche anzurufen messageMe Methode aus nativem Code während des getJniString*Methodenaufrufs von Java nach nativ aufzurufen.

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

Nach der sauberen Kompilierung stoppt die App mit der nächsten Nachricht:

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)

Anscheinend bedeutet dies, dass der Methodenname falsch ist, aber er sieht für mich in Ordnung aus.

Denys S.
quelle
21
Bitte veröffentlichen Sie Ihre Lösung als normale Antwort, damit sowohl Ihre Frage als auch die Lösung leichter zu lesen und somit für die Community nützlicher sind. Sie können auch mit anderen Personen zusammenarbeiten, die bereits geantwortet haben, um ihre Antworten zu vervollständigen.
misiu_mp
@Denys: Ich habe Ihre Codierung befolgt, aber ich erhalte die folgende Fehlermeldung: java.lang.UnsatisfiedLinkError: getJniString. Können Sie mir helfen, diesen Fehler zu beheben?
Huy Tower
@AlexTran, es ist lange her, aber nach dem Fehler zu urteilen, haben Sie die getJniStringMethode wahrscheinlich falsch geschrieben oder weder in Java noch in c verknüpft . Stellen Sie sicher, dass Sie den C-Code wahrscheinlich durch Systemimport ordnungsgemäß mit Java verknüpfen (bitte erinnern Sie sich jetzt nicht an all diese Dinge: P)
Denys S.
1
Wie ruft dies eine Java-Methode von c auf? Es ist offensichtlich Javas onCreateMethode, die Ihre Mutter C. aufruft
John
Ich erhalte den Basisoperanden '->' mit dem Nichtzeigertyp 'JNIEnv', wenn er mit der Umgebungsvariablen (env) ausgeführt wird. Was ist auch, wenn Sie auf die Variable env * verzichten möchten, z. B. einen Rückruf von JNI auf die Java-Ebene? Irgendein Vorschlag!
CoDe

Antworten:

45

Wenn es sich um eine Objektmethode handelt, müssen Sie das Objekt übergeben an CallObjectMethod:

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

Was Sie getan haben, war das Äquivalent von jstr.messageMe().

Da Ihre Methode nichtig ist, sollten Sie Folgendes aufrufen:

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

Wenn Sie ein Ergebnis zurückgeben möchten, müssen Sie Ihre JNI-Signatur (dh ()Veine Methode für den voidRückgabetyp) und auch den Rückgabetyp in Ihrem Java-Code ändern .

Matthew Willis
quelle
Bitte, führe mich, wie das geht, wegen meiner PS :)
Denys S.
Ich erhalte das gleiche Ergebnis mit dem, was Sie vorschlagen.
Denys S.
1
Es gibt tatsächlich eine CallVoidMethod, CallObjectMethod usw. mit jeweils einem anderen Rückgabetyp. Da Ihre messageMe-Methode (Ljava / lang / String;) V ist, müssen Sie CallVoidMethod verwenden.
Matthew Willis
2
Beachten Sie, dass der Fehler, den Sie erhalten, wahrscheinlich darauf hinweist, dass Ihre native Java-Methode (in Ihrem Java-Code) wahrscheinlich nicht vom Typ void return ist und daher von GetMethodID
Matthew Willis am
10

Lösung von Denys S. im Fragenbeitrag:

Ich habe es mit der Konvertierung von C in C ++ (im Grunde genommen envvariables Zeug) ziemlich durcheinander gebracht , aber ich habe es mit dem folgenden Code für C ++ zum Laufen gebracht:

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

Und der nächste Code für Java-Methoden:

    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
quelle
Müssen nativeMethoden statisch sein?
IgorGanapolsky