Search code examples
javaandroidc++jvmjava-native-interface

Calling a JAVA method from C++ with JNI, no parameters


Please bear with me, I am an iPhone developer and this whole android this confuses me a bit.

I have some c++ methods that are called from a cocos2d-x CCMenuItem. Therefore I cannot send any parameters, according to the docs.

I need to open a url with the android browser which will require me to call a JAVA function to start a new intent.

I understand that I need to create a VM, however the below code gives me the error:

jni/../../Classes/OptionsScene.cpp:184: error: 'JNI_CreateJavaVM' was not declared in this scope

I was looking at this thread: Calling a java method from c++ in Android

But he uses parameters, and I can't do that. And I don't see where those are in his code to just make them myself.

I don't know what the string should be in the 'Find Class' method. Also, I assume it is pretty terrible to create a new VM instance in every method I need to call. How would I create one as a singleton to use across the board?

This is my c++ code called by my menu item:

#include <jni.h>
...
JavaVM *vm; // Global
...
void OptionsScene::website(){
JNIEnv *env;
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 0;
vm_args.ignoreUnrecognized = 1;

jint result = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args); // This line still errors

jclass clazz = env->FindClass("com/prndl/project/WebExecute");
jmethodID method = env->GetMethodID(clazz, "website", "(Ljava/lang/String;)V");
env->CallVoidMethod(NULL,method);

vm->DestroyJavaVM();

And this is the JAVA Method that I need to call:

public class WebExecute extends Activity{
    public void website(){
        Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));
        startActivity(browserIntent);
    }
}

Honestly, I am struggling with this, any help is appreciated. Thanks.


Solution

  • A number of things...

    1. Given the declaration JNIEnv* env;, and given that you're in C++, you use it as env->FindClass(someString), not how you're doing it. If it was C you'd use FindClass(env, someString) but in C++ you use env->FindClass(someString).
    2. The string to use in FindClass is the fully qualified path name but with / as the delimiter instead of . For example, if the class is Foo in package bar.baz.quux, the fully-qualified name is bar.baz.quux.Foo and the string you'd give to FindClass is bar/baz/quux/Foo.
    3. You can only create one JVM per C++ process. I'm pretty sure you need to create a single JVM one time. You will therefore need to have the JavaVM* vm be a global variable (or at least be somewhere accessible to everything that needs to use. Everything in the same C++ thread as the thread that called JNI_CreateJavaVM() will use the JNIEnv * that gets filled in by that call. Every other thread that wants to use the JVM needs to call AttachCurrentThread which will bind that thread to the JVM and fill in a new JNIEnv * valid for that thread.
    4. Have you double-checked your compiler/IDE settings to make sure that the JDK_HOME/include directory (which contains jni.h) is in the includes search path? Same for the JDK_HOME/include/android directory (or whatever the operating specific directory in JDK_HOME/include is called in a Android JDK)?

    A very useful resource is The JNI book

    But be careful while reading it because some examples are in C and some are in C++, so make sure you understand how the calling conventions differ.