开发者

JNI crashes when calling CallVoidMethod

开发者 https://www.devze.com 2023-04-06 19:50 出处:网络
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.

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.


In my case I was calling a Kotlin function. And to call a kotlin function you need to write @JvmStatic before function name in kotlin.

Kotlin code

@JvmStatic
fun ReceiveDataFromCpp(data: ShortArray)
{
    Log.d("Kotlin array Return -----> ", "arr: " + data
        .contentToString()
    );
}

Cpp code

JNIEnv * g_env;
g_env = getEnv();


 jmethodID jmethodId = g_env->GetStaticMethodID(clientClass, "ReceiveDataFromCpp",
                                        "([S)V");
 if (jmethodId == NULL)
 {
     return;
 }

 jshortArray dataArray = nullptr;
 dataArray = g_env->NewShortArray(480);
 g_env->SetShortArrayRegion(dataArray, 0, 480, mData);
 g_env->CallStaticVoidMethod(clientRecorderClass, jmethodId, dataArray);
0

精彩评论

暂无评论...
验证码 换一张
取 消