Llamar a un método java desde c ++ en Android

91

Estoy tratando de obtener una simple llamada al método Java desde C ++ mientras Java llama al método nativo. Aquí está el código de 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();
}

Estoy tratando de llamar al messageMemétodo desde el código nativo en el proceso de getJniString*llamada al método desde Java al nativo.

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

Después de que la aplicación de compilación limpia se detiene con el siguiente mensaje:

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)

Aparentemente, significa que el nombre del método es incorrecto, pero me parece que está bien.

Denys S.
fuente
21
Publique su solución como una respuesta normal para que tanto su pregunta como la solución sean más fáciles de leer y, por lo tanto, más útiles para la comunidad. También puede colaborar con otras personas que ya respondieron para completar sus respuestas.
misiu_mp
@Denys: Seguí tu codificación, pero obtengo este error: java.lang.UnsatisfiedLinkError: getJniString. ¿Puedes ayudarme a corregir este error?
Torre Huy
@AlexTran, fue hace mucho tiempo, pero a juzgar por el error, probablemente escribió mal o no vinculó el getJniStringmétodo en java o en c. Asegúrese de vincular correctamente el código c a java probablemente mediante la importación del sistema (srsly no recuerdo todo esto ahora: P)
Denys S.
1
¿Cómo se llama a un método java desde c? Es descaradamente de Java onCreatemétodo de llamar a su nativa C.
John
Obtengo el operando base de '->' tiene un tipo no puntero 'JNIEnv cuando se ejecuta con la variable de entorno (env). También, ¿qué pasa si quisiera prescindir de la variable env *, como la devolución de llamada de JNI a la capa Java? ¡Cualquier sugerencia!
Código

Respuestas:

45

Si es un método de objeto, debe pasar el objeto a CallObjectMethod:

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

Lo que estabas haciendo era el equivalente a jstr.messageMe().

Dado que su es un método vacío, debe llamar a:

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

Si desea devolver un resultado, debe cambiar su firma JNI (lo que ()Vsignifica un método de voidtipo de retorno) y también el tipo de retorno en su código Java.

Matthew Willis
fuente
Por favor, guíame sobre cómo hacer eso, debido a mi PD :)
Denys S.
Obtengo el mismo resultado con lo que sugieres.
Denys S.
1
en realidad, hay un CallVoidMethod, CallObjectMethod, etc., cada uno con un tipo de retorno diferente. Dado que su método messageMe es (Ljava / lang / String;) V, debe usar CallVoidMethod.
Matthew Willis
2
tenga en cuenta que el error que está recibiendo probablemente indica que su método nativo de Java (en su código de Java) probablemente no sea del tipo de retorno nulo y, por lo tanto, GetMethodID no lo encuentre
Matthew Willis
10

Solución publicada por Denys S. en la publicación de preguntas:

Lo arruiné bastante con la conversión de c a c ++ (básicamente envcosas variables), pero lo hice funcionar con el siguiente código para 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);
}

Y el siguiente código para los métodos de 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
fuente
¿Los nativemétodos tienen que ser estáticos?
IgorGanapolsky