Calling a java method from c++ in Android

113,339

Solution 1

If it's an object method, you need to pass the object to CallObjectMethod:

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

What you were doing was the equivalent of jstr.messageMe().

Since your is a void method, you should call:

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

If you want to return a result, you need to change your JNI signature (the ()V means a method of void return type) and also the return type in your Java code.

Solution 2

Solution posted by Denys S. in the question post:

I quite messed it up with c to c++ conversion (basically env variable stuff), but I got it working with the following code for 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);
}

And next code for java methods:

    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();
}
Share:
113,339
Denys S.
Author by

Denys S.

We keep moving forward, opening new doors, and doing new things, because we're curious and curiosity keeps leading us down new paths. (Walt Disney) Imagination is more important than knowledge. For knowledge is limited to all we now know and understand, while imagination embraces the entire world, and all there ever will be to know and understand. (Albert Einstein)

Updated on March 14, 2020

Comments

  • Denys S.
    Denys S. about 4 years

    I'm trying to get a simple Java method call from C++ while Java calls native method. Here's the 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();
    }
    

    I'm trying to call messageMe method from native code in the process of getJniString* method call from Java to native.

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

    After clean compilation app stops with next message:

    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)
    

    Apparently it means that method name is wrong, but it looks OK to me.

  • Denys S.
    Denys S. about 13 years
    Please, guide me on how to do that, because of my P.S. :)
  • Denys S.
    Denys S. about 13 years
    I get the same result with what you're suggesting.
  • Matthew Willis
    Matthew Willis about 13 years
    there is actually a CallVoidMethod, CallObjectMethod, etc. each one with a different return type. Since your messageMe method is (Ljava/lang/String;)V, you need to use CallVoidMethod.
  • Matthew Willis
    Matthew Willis about 13 years
    note that the error you're getting probably indicates that your Java native method (in your Java code) is probably not of void return type and so isn't being found by GetMethodID
  • IgorGanapolsky
    IgorGanapolsky about 8 years
    Do native methods have to be static?