Вызов java-метода из c ++ в Android

91

Я пытаюсь получить простой вызов метода Java из C ++, в то время как Java вызывает собственный метод. Вот код 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();
}

Я пытаюсь вызвать messageMeметод из собственного кода в процессе getJniString*вызова метода из Java в собственный.

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

После чистой компиляции приложение останавливается со следующим сообщением:

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)

По-видимому, это означает, что имя метода неправильное, но мне кажется, что это нормально.

Денис С.
источник
21
Опубликуйте свое решение как обычный ответ, чтобы облегчить чтение и ваш вопрос, и решение, а значит, сделать его более полезным для сообщества. Вы также можете сотрудничать с другими людьми, которые уже ответили, чтобы завершить свои ответы.
misiu_mp
@Denys: Я следил за вашим кодированием, но получаю эту ошибку: java.lang.UnsatisfiedLinkError: getJniString. Можете ли вы помочь мне исправить эту ошибку?
Huy Tower
@AlexTran, это было давно, но, судя по ошибке, вы, вероятно, неправильно написали или не связали getJniStringметод ни в java, ни в c. Убедитесь, что вы правильно связали код c с java, вероятно, с помощью системного импорта (правда, не помню все это сейчас: P)
Денис С.
1
Как это вызов java-метода из c? Это явный onCreateметод Java, вызывающий ваш родной C.
Джон
Я получаю базовый операнд '->' имеет тип без указателя 'JNIEnv при выполнении с переменной окружения (env). А как насчет того, чтобы обойтись без переменной env *, например обратного вызова из JNI в уровень Java! Любое предложение!
CoDe

Ответы:

45

Если это метод объекта, вам необходимо передать объект в CallObjectMethod:

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

То, что вы делали, было эквивалентом jstr.messageMe().

Поскольку ваш метод является недействительным, вы должны вызвать:

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

Если вы хотите вернуть результат, вам нужно изменить свою подпись JNI ( ()Vозначает метод типа voidвозвращаемого значения), а также тип возвращаемого значения в коде Java.

Мэтью Уиллис
источник
Пожалуйста, посоветуйте мне, как это сделать, из-за моего PS :)
Денис С.
Я получаю тот же результат, что и вы.
Денис С.
1
на самом деле существует CallVoidMethod, CallObjectMethod и т. д., каждый из которых имеет свой тип возвращаемого значения. Поскольку ваш метод messageMe - (Ljava / lang / String;) V, вам необходимо использовать CallVoidMethod.
Мэтью Уиллис,
2
обратите внимание, что ошибка, которую вы получаете, вероятно, указывает на то, что ваш собственный метод Java (в вашем коде Java), вероятно, не имеет возвращаемого типа void и поэтому не может быть найден GetMethodID
Мэтью Уиллис,
10

Решение, опубликованное Денисом С. в вопросе:

Я сильно испортил это с преобразованием c в c ++ (в основном, envпеременные), но у меня он работал со следующим кодом для 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);
}

И следующий код для 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
источник
Должны ли nativeметоды быть статичными?
Игорь Ганапольский