JNI - CallVoidMethod = Fatal signal 11

I am trying to call a non static java method from c++. Static calls work fine.

To accomplish this I have the following in c++

#include <jni.h>
extern
"C"
{
    JavaVM* javaVM = NULL;
    jclass activityClass;
    jobject activityObj;

     JNIEXPORT void Java_com_thepackagename_theclassname_initJNIBridge(JNIEnv *env, jobject jobj){
        env->GetJavaVM(&javaVM);
        jclass cls = env->GetObjectClass(jobj);
        activityClass = (jclass) env->NewGlobalRef(cls);
        activityObj = env->NewGlobalRef(jobj);

        return;
    }

    void GameScene::purchaseItem(void) {
        JNIEnv *env;
        javaVM->AttachCurrentThread(&env, NULL);
        jmethodID method = env->GetMethodID(activityClass, "UnlockGame", "(Ljava/lang/String;)V");
        jstring StringArg1 = env->NewStringUTF("product1");

        env->CallVoidMethod(activityObj, method, StringArg1);
        //env->CallObjectMethod(activityObj, method, StringArg1);
}

The java method is:

void UnlockGame(String textStr){
    Log.d(TAG, "UnlockGame CALLED!");
}

So in the java onCreate method of Cocos2dxActivity I call the initJNIBridge c++ method and everything is fine. Later, when I call the UnlockGame java method from c++ I get the following error:

A/libc(28959): Fatal signal 11 (SIGSEGV) at 0x002447a8 (code=1), thread 28974 (Thread-1031)

The error is on the actual CallVoidMethod line, if I comment it out it doesn’t crash.

I have tried various ways to call UnlockGame including removing the arguments (from the signature and the call), tried CallVoidMethod and CallObjectMethod, nothing works. I have done a ndk-stack and it tells me nothing other than the app crashes in the purchaseItem method, which I already knew.

Can anyone suggest what I might be doing wrong?

Thanks

#include
//declare this as global will use this later
jclass activityClass;

JNIEnv thisEnv;
jobject activityObj; 

extern
"C"
{
     JNIEXPORT void Java_com_thepackagename_theclassname_initJNIBridge(JNIEnv *env, jobject jobj){
        thisEnv=env;
        activityObj=jobj;

    }
}
    void GameScene::purchaseItem(void) {
        JavaVM* javaVM ;
        JNIEnv *env;
        thisEnv->getJavaVM(&javaVM);
        javaVM->AttachCurrentThread(&thisEnv, 0);
        jclass cls = thisEnv->GetObjectClass(activityObj);
        jmethodID method = thisEnv->GetMethodID(cls, "UnlockGame", "(Ljava/lang/String;)V");
        jstring StringArg1 = thisEnv->NewStringUTF("product1");

        env->CallVoidMethod(activityObj, method, StringArg1);
        //env->CallObjectMethod(activityObj, method, StringArg1);
}

note dont put void GameScene::purchaseItem(){} inside of the Extern C

Thanks for the reply.

I had previously tried moving GameScene::purchaseItem() outside of the Extern C but I will do it again. I think you’re right about the global declarations. I wonder though why it allowed me to use the jclass and activityObj without crashing prior to making the call, like with activityClass here:

jmethodID method = env->GetMethodID(activityClass, "UnlockGame", "(Ljava/lang/String;)V");

In any case, I’m looking forward to trying the changes you suggest. I’m at work and won’t be home until later today but I will post the results.

Thanks again for taking the time to reply and providing code, much appreciated.

Well I don’t know if this is progress or not but now I get a different error

Fatal signal 6 (SIGABRT) at 0x00002e42 (code=-6), thread 11858 (Thread-851)

And there is a warning in the log:

03-26 19:57:07.195: W/dalvikvm(11842): JNI WARNING: threadid=11 using env from threadid=1 (GetJavaVM)
03-26 19:57:07.195: W/dalvikvm(11842):              in Lorg/cocos2dx/lib/Cocos2dxRenderer;.nativeRender:()V (GetJavaVM)
03-26 19:57:07.205: I/dalvikvm(11842): "GLThread 851" prio=5 tid=11 NATIVE
03-26 19:57:07.205: I/dalvikvm(11842):   | group="main" sCount=0 dsCount=0 obj=0x423f9e40 self=0x730e4ca8

I set everything up to match your example, changing only the things that needed changing to allow it to compile, such as getJavaVM to GetJavaVM, etc.

When that didn’t work I then tried changing things like using thisEnv to call the method instead of env.

Nothing is working and I’ve now wasted several days on this. Does anyone have a working example of making a non-static call from c++ to java (preferably using cocos2d-x). A really simple bare bones example that does nothing other than successfully call a method?

I’ve seen numerous examples but they all have code snippets that seem to leave out important details as I can’t get it to work. That’s where my original code was from, an example on another site https://developer.vuforia.com/forum/faq/android-how-can-i-call-java-methods-c.

Thanks.

@notamonopoly
here is how I call my java function in c++ using jni

JavaVM *jvm; //this is important to avoid threading errors
thisEnv->GetJavaVM(&jvm); //this is important to avoid threading errors
jvm->AttachCurrentThread(&thisEnv, 0); //this is important to avoid threading errors
//jstring jstr = thisEnv->NewStringUTF(“From jni”);
jclass cls = thisEnv->GetObjectClass(thisObj);
jmethodID method = thisEnv->GetMethodID(cls, “methodFromJAVA”,"(Ljava/lang/String;)V");
jobject result = thisEnv->CallObjectMethod(thisObj, method);

note: “env->CallVoidMethod(activityObj, method, StringArg1)” remove this line in your code, there’s no need for you to use this, by calling this line “thisEnv->GetMethodID(cls, “methodFromJAVA”,”(Ljava/lang/String;)V");" you are already specifying that your code is void and has a string parameter

To simplify things I created a new HelloWorld project called jnitest.

This is the relevant part of the cpp file

#include "HelloWorldScene.h"

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform\android\jni\JniHelper.h"
#endif

USING_NS_CC;

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

jclass activityClass;
jobject activityObj;
JNIEnv* thisEnv;

extern
"C"
{
    JNIEXPORT void JNICALL Java_com_testing_jnitest_jnitest_initJNIBridge(JNIEnv *env, jobject jobj){
        thisEnv=env;
        activityObj=jobj;
    }
}

void HelloWorld::makeJniCall(void) {
    JavaVM *jvm; //this is important to avoid threading errors
    thisEnv->GetJavaVM(&jvm); //this is important to avoid threading errors
    jvm->AttachCurrentThread(&thisEnv, 0); //this is important to avoid threading errors

    jclass cls = thisEnv->GetObjectClass(activityObj);
    jmethodID method = thisEnv->GetMethodID(cls, "UnlockGame","(Ljava/lang/String;)V");
    jobject result = thisEnv->CallObjectMethod(activityObj, method);
}

#endif

And the result from calling makeJniCall:

03-27 21:38:44.239: W/dalvikvm(19263): JNI WARNING: threadid=11 using env from threadid=1 (GetJavaVM)
03-27 21:38:44.239: E/dalvikvm(19263): JNI ERROR: env->self != thread-self (0x416a75d0 vs. 0x73bb3aa8); auto-correcting
03-27 21:38:44.239: A/libc(19263): Fatal signal 11 (SIGSEGV) at 0x00000004 (code=1), thread 19278 (Thread-986)

I believe I’ve done everything you’ve suggested. Am I missing something. Do you have any other suggestions, or possibly a full working example (.cpp and .java file)?

okay here is my working code

and the java code

in java code look for these functions:
methodFromJAVA()->java/android function that will call camera intent
getPath()->this will return a string

in c++ code look for the following method:
HelloWorld::printSomethingFromJava()->this will call the methodfromjava
HelloWorld::displayImage()->this will call the getPath

hope it helps

can I also see your java code?if its okay

@notamonopoly
please also take note:
public native void initJNIBridge();->this is the native function that connect your java to c++

be sure that you call this method inside of your java call it’s either in onCreate() or onResume(), else you’ll get an error.

@ranger

Wow, thanks. That’s more than I was expecting. I just got to work but I will look at it later today and see if I can get my code to finally work.

I can show you my java code as well. I didn’t include it before because there wasn’t anything in it except the UnlockGame method and a call to initJNIBridge in the onCreate method, but I should have added it here to be complete.

Just taking a quick look it seems that I have my code setup the same way as yours but if yours works then it’s clearly something I’ve done wrong.

I will let you know when I get it working. Thanks again for following up, you’ve already been a big help.

I finally got it working!

Using the relevant parts from your code I was able to successfully make a non-static call to Java from C++. I did get an error, though the app didn’t crash:

03-28 21:44:24.673: W/dalvikvm(20566): JNI WARNING: threadid=11 using env from threadid=1 (GetJavaVM)
03-28 21:44:24.673: E/dalvikvm(20566): JNI ERROR: env->self != thread-self (0x415ba5f8 vs. 0x7481b488); auto-correcting

So after doing some research I created a global reference to the jvm when making the first jni call and then reuse it in all other calls.

JNIEnv* thisEnv;
jobject thisObj;
JavaVM  *jvm;

extern "C" {
    JNIEXPORT jstring JNICALL
    Java_com_testing_jnitest_jnitest_stringFromJNI(JNIEnv *env, jobject obj)
    {
        thisEnv=env;
        thisObj=obj;
        env->GetJavaVM(&jvm);
        return env->NewStringUTF("Hello from C++ over JNI!");
    }
}

void HelloWorld::displayImage() {
    jvm->AttachCurrentThread(&thisEnv, 0); //this is important to avoid threading errors
    jclass cls = thisEnv->GetObjectClass(thisObj);
    jmethodID method = thisEnv->GetMethodID(cls, "getPath","()Ljava/lang/String;");
    jstring result = (jstring) thisEnv->CallObjectMethod(thisObj, method);
}

The java wasn’t changed much from your code.

public class jnitest extends Cocos2dxActivity{
    //JNI method
    public native String stringFromJNI();
    public String path = "Testing";
	
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        System.out.println("The string = " + stringFromJNI());
    }

    public Cocos2dxGLSurfaceView onCreateView() {
    	Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);
    	// jnitest should create stencil buffer
    	glSurfaceView.setEGLConfigChooser(5, 6, 5, 0, 16, 8);
    	
    	return glSurfaceView;
    }

    static {
        System.loadLibrary("cocos2dcpp");
    }

    public String getPath(){
    	System.out.println("getPath Called!");
    	return path;
    }
}

@ranger
Thank you so much for all your help. This was driving me absolutely nuts!

@notamonopoly
Your welcome sir

Hey Guys, I just experienced a very similar problem after I updated to Cocos2dxActivity from NativeActivity. (based on cocos2d-develop code)

The error I’m getting is the standard memory issue Fatal Signal 11 (SIGSEGV). I tracked it down to the JNI call from the renderer, which fails trying to do callback the cocos director, but works just fine with random code.

JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeOnResume() {
        int a=5;
        a=a+5;
}

but fails when I try to get the original cocos calls.

JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeOnResume() {
        if (Director::getInstance()->getOpenGLView()) {
            Application::getInstance()->applicationWillEnterForeground();
        }
}

Here is my main activity.

public class AEActivity extends Cocos2dxActivity {
    
    public Cocos2dxGLSurfaceView onCreateView() {
        Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);
        // TestCpp should create stencil buffer
        glSurfaceView.setEGLConfigChooser(5, 6, 5, 0, 16, 8);
        
        return glSurfaceView;
    }
}

I’d be super grateful for any hints.

Did you ever discover a solution? I believe I’m running into something similar.

Yes, I believe this was due to the Java activity they used. They then reverted back to the original. I think they introduced it in 3.0beta and went back to the simple one in 3.0rc0. I’m a little hazy on details since it’s been a while. If you really get stuck and this doesn’t help, I can probably look at my code.

Thanks for the prompt response.

I solved my problem and it turns out it was unrelated to this. It had something to do with building JNI signatures and package names programmatically (still not 100% sure).

my code is not working .Plz give me full implemention with classes…