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.
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:
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?
@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)?
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
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.
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!
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.
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.
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).