调用CallVoidMethod时JNI崩溃

9

我将尝试在Android应用程序中从本地C代码调用Java方法。使用JNI似乎很简单,但当最终调用方法时,我的代码总是崩溃。 以下是我的代码: 本地C代码:

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 代码:

public void showCar(){      
    doSomething()
}

doSomething() 函数甚至没有被执行,我可以在那里设置断点,但它永远不会被触发。正如上面所述,只要我注释掉 CallVoidMethod 调用,它就不会崩溃,但显然也不会调用 showCar() 函数。有什么提示吗?


你确定FindClassGetMethodID返回的不是空值吗? - Stuart Cook
是的,我们已经检查了这两个结果,但其中似乎有二进制数据或其他内容。虽然它绝对不是空的。不幸的是,使用Android NDK和GDB进行本地代码调试证明非常困难,因为我们根本无法让C调试器起作用。 - Lennart
2个回答

11

以下是4个建议:

...

jclass clazz = env->FindClass("com/path/to/the/class");

请确认名称不是"com/path/to/the/MyClass",其中类名的首字母大写,并且"name"是一个保留字。在您的示例中,JNI C符号名称"Java_com_path_to_my_package_renderStuff"的使用方式与"FindClass()"查找"com/path/to/the/class"存在轻微差异。但由于您的stackoverflow问题与UnsatisfiedLinkageError无关,因此我只能猜测您提供的示例本身不一致。

使用我的示例,我预计JNI C符号名称为"Java_com_path_to_the_MyClass_renderStuff",并且"FindClass()"查找的内容为"com/path/to/the/MyClass"。类名以大写字母开头,方法名以小写字母开头可能对于链接很重要。

...

您确定传递的"jobj"与您查找的"com/path/to/the/class"是相同类型的吗?也许您可以在Java代码中使用以下方法来包装您的本地方法:

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();

如何在不导致JVM崩溃的情况下确保Java代码中的问题。您还需要调整C符号名称,将“_1internal”附加到末尾,使其变为“Java_com_path_to_the_MyClass_renderStuff_1internal”(意在多余的“1”字符)。

...

也许可以在每个列出的语句之间进行多重异常检查:

if(env->ExceptionCheck()) {
    env->ExceptionDescribe();
    env->ExceptionClear();
}
这将会捕捉到一些安全违规,例如在可能不允许使用反射的情况下尝试使用它。
 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);

另一个去除FindClass()调用的想法。这种方法适用于任何GetMethodID可用的类,就像动态类型/后期绑定一样。


使用 GetObjectClass() 而不是 FindClass 是我需要知道的。 - Alyoshak
如果使用env->GetObjectClass(jobj)会出现递归循环,而且showCar方法从未被调用。 - Anonimys

2
在我的情况下,我在调用一个Kotlin函数。为了调用Kotlin函数,你需要在函数名前写@JvmStatic
Kotlin代码
@JvmStatic
fun ReceiveDataFromCpp(data: ShortArray)
{
    Log.d("Kotlin array Return -----> ", "arr: " + data
        .contentToString()
    );
}

Cpp 代码

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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接