I’m trying to call a java method from native C code in an Android Application. This sounds quite simple with the usage of JNI, but my code always crashes when finally calling the method itself.
Here’s my code:
Native C Code:
JNIEXPORT void JNICALL
Java_com_path_to_my_package_renderStuff(JNIEnv* env, jobject jobj){
//...
jclass clazz = env->FindClass("com/path/to/the/class");
jmethodID showCar = env->GetMethodID(clazz,"showCar","()V" );
env->CallVoidMethod(jobj,showCar); //If I comment this out, it won't crash
//...
}
Java Code:
public void showCar(){
doSomething()
}
doSomething() isn’t even reached, I can set a breakpoint there, which will never be hit. And as said above, as soon as I comment out the CallVoidMethod call, it won’t crash but obviously not call showCar() either. Any hints?
4 ideas to provide you:
…
jclass clazz = env->FindClass(“com/path/to/the/class”);
Can you confirm the name is not “com/path/to/the/MyClass” where the classname is uppercase 1st character and obviously the name “class” is a reserved word. There is a slight discrepency between the use of the JNI C symbol name “Java_com_path_to_my_package_renderStuff” and the FindClass() lookup on “com/path/to/the/class”in your example. But since your stackoverflow is not a about UnsatisfiedLinkageError I can only guess your example provided is not consistent with itself.
Using my example I would expect the JNI C symbol name to be “Java_com_path_to_the_MyClass_renderStuff” and the FindClass() lookup on “com/path/to/the/MyClass”. The use of uppercase 1st letter of class and lowercase 1st letter of method name might be important for linkage purposes.
…
Are you sure the “jobj” being passed is the same type as the “com/path/to/the/class” you are looking up ? Maybe in your Java code you can wrap your native with:
public void renderStuff() {
if((this instanceof com.path.to.the.MyClass) == false)
throw new RuntimeException("Unexpected class expected: com.path.to.the.MyClass");
renderStuff_internal();
}
private native void renderStuff_internal();
Which will ensure that matter in Java code without causing a JVM crash. You would also need to adjust your C symbol name to append the “_1internal” onto the end making “Java_com_path_to_the_MyClass_renderStuff_1internal” (the extra “1” character is intended)
…
Maybe try belt and braces exception checking in between each statement you list about:
if(env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
This will pickup things like security violations when trying to do reflection when it might not be allowed.
…
jclass cls = env->GetObjectClass(jobj); // instead of FindClass
jmethodID mid = env->GetMethodID(cls, "showCar", "()V");
if(!mid) return; // whoops method does not exist
env->CallVoidMethod(jobj, mid);
Another idea to remove the FindClass() call. This would work with any class that GetMethodID worked on, kind of like dyhamic typing / late-binding.
Tags: androidandroid