Search code examples
android-ndkjava-native-interface

How to call java function and pass arguments from c++ using Android NDK


I try to call a java function from my c++ code, but the app keeps 'crashing'.

At first, I start the c++ code through JNI call, which works without any problem. Then I let the function which is called executing the callback:

#include <jni.h>

extern "C"
JNIEXPORT jstring JNICALL
Java_net_example_folder_Service_startSomething(JNIEnv *env, jobject obj) {
    invoke_class(env);
    return env->NewStringUTF("The End.\n");    //works if I only use this line
}

Trying to follow http://www.inonit.com/cygwin/jni/invocationApi/c.html (and a lot of other guides/tips etc.), I use this to call the java function:

void invoke_class(JNIEnv* env) {
    jclass helloWorldClass;
    jmethodID mainMethod;

    helloWorldClass = env->FindClass("Java/net/example/folder/helloWorldClass");

    mainMethod = env->GetStaticMethodID(helloWorldClass, "helloWorld", "()V");

    env->CallStaticVoidMethod(helloWorldClass, mainMethod);
}

To call the java code:

package net.example.folder;

import android.util.Log;

public class helloWorldClass {
    public static void helloWorld() {
        Log.e("helloWorldCLass", "Hello World!");
    }
}

The c++ code is called by a background service. Here is the function of the Activity that starts it:

public void startService() {
    Intent i = new Intent(this, Service.class);
    startService(i);
}

And this is a part of the Service:

public class SimService extends IntentService {

    ...

    @Override
    protected void onHandleIntent(Intent intent) {
        System.loadLibrary("native-lib");
        startSomething();
    }
}

That all works, but when I now change the function 'invoke_class' to:

void invoke_class(JNIEnv* env) {
    jclass helloWorldClass;
    jmethodID mainMethod;

    helloWorldClass = env->FindClass("net/example/folder/helloWorldClass");

    mainMethod = env->GetStaticMethodID(helloWorldClass, "helloWorld", "([Ljava/lang/String;)V");

    env->CallStaticVoidMethod(helloWorldClass, mainMethod, env->NewStringUTF("some text"));
}

and of course the java part to:

package net.example.folder;

import android.util.Log;

public class helloWorldClass {
    public static void helloWorld(String msg) {
        Log.e("helloWorldCLass", msg);
    }
}

With that, I'll get the earlier mentioned crash.

Why is that? How do I pass arguments correctly?


Solution

  • Symbol [ is used to represent arrays (as if you had String[] msg), remove that from your GetStaticMethodID method.

    You can see more information about JNI Types and Data Structures here

    Also as Michael said - you should check for java exceptions in your native code, because otherwise your app will crash. You can do it using Exception jni functions. For example:

    jclass exClass = env->FindClass("some/random/class");
    if (exClass == nullptr || env->ExceptionOccurred()) {
        env->ExceptionClear();
        // do smth in case of failure
    }