我有一个回调类,用于执行从本机C ++代码到Kotlin的回调(不确定Kotlin / Java是否在这里有所作为,如果有的话,是否有任何文档?)。我有一个带有整数参数的工作回调,可以从其他本机线程调用而没有问题。现在,我想添加第二个发送一个String的字符串,但是由于某种原因它不起作用。
我的回调类实现如下:
jclass target;
jmethodID id;
Callback::Callback(JavaVM &jvm, jobject object) : g_jvm(jvm), g_object(object) {
JNIEnv *g_env;
int getEnvStat = g_jvm.GetEnv((void **) &g_env, JNI_VERSION_1_6);
if (g_env != NULL) {
target = g_env->GetObjectClass(g_object);
id = g_env->GetMethodID(target, "integerCallback", "(I)V");
}
}
void Callback::call(int integerValue, const char *stringValue) {
JNIEnv *g_env;
int getEnvStat = g_jvm.GetEnv((void **) &g_env, JNI_VERSION_1_6);
if (getEnvStat == JNI_EDETACHED) {
if (g_jvm.AttachCurrentThread(&g_env, NULL) != 0) {
LOGD("GetEnv: Failed to attach");
}
} else if (getEnvStat == JNI_OK) {
LOGD("GetEnv: JNI_OK");
} else if (getEnvStat == JNI_EVERSION) {
LOGD("GetEnv: version not supported");
}
g_env->CallVoidMethod(g_object, id, (jint) integerValue);
}
它像这样在我的native-lib.cpp
中实例化:
extern "C" {
std::unique_ptr<Callback> callback;
JavaVM *g_jvm = nullptr;
static jobject myJNIClass;
jint JNI_OnLoad(JavaVM *pJvm, void *reserved) {
g_jvm = pJvm;
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL
Java_com_my_app_common_jni_JniBridge_loadNative(JNIEnv *env, jobject instance,
jstring URI, jboolean isWaitMode) {
myJNIClass = env->NewGlobalRef(instance);
if (callback == nullptr) {
callback = std::make_unique<Callback>(*g_jvm, myJNIClass);
}
}
它讨论的回调方法在我的JniBridge.kt
中(线程部分可能与问题无关):
object JniBridge {
init {
System.loadLibrary("native-lib")
Timber.d("Native Lib loaded!")
}
fun load(fileName: String, isWaitMode: Boolean) {
loadNative(fileName, isWaitMode)
}
fun integerCallback(value: Int) {
someListener?.invoke(value)
}
private external fun loadNative(fileName: String, isWaitMode: Boolean)
}
所以现在,如果我的本机代码在我的回调中触发了call()
方法,则integerCallback()
中的JniBridge.kt
将被正确地使用整数调用。
但是这是我没有得到的:如果我更改回调以发送String,则无法使用。如果我这样更改:
// in JniBridge.kt
fun stringCallback(value: String) {
someListener?.invoke(value)
}
// in Callback.cpp
//getting the method
id = g_env->GetMethodID(target, "stringCallback", "(Ljava/lang/String)V");
//making the call
g_env->CallVoidMethod(g_object, id, (jstring) stringValue);
现在我的应用因此错误而崩溃:
JNI DETECTED ERROR IN APPLICATION: JNI GetStringUTFChars called with pending exception java.lang.NoSuchMethodError: no non-static method "Lcom/my/app/common/jni/JniBridge;.stringCallback(Ljava/lang/String)V"
[如果我尝试调用void
方法(例如:id = g_env->GetMethodID(target, "voidCallback", "(V)V");
或调用需要两个整数的方法(例如:id = g_env->GetMethodID(target, "twoIntegerCallback", "(I;I)V");
,当然会在JniBridge.kt
中使用相应的方法),也会发生同样的情况。
为什么会发生,如何解决?
注意:为清楚起见,我已经忽略了我认为与该问题无关的代码的所有部分,如果缺少一些关键的信息,请告诉我并予以解决。
您传递给GetMethodID
的方法签名中缺少分号。
应该是:
id = g_env->GetMethodID(target, "stringCallback", "(Ljava/lang/String;)V");
注意Ljava/lang/String
后的分号。
请参见Oracle's JNI documentation中有关类型签名的部分。
关于您的其他变化:
Java void function()
将具有签名()V
。Java void function(int i, int j)
将具有签名(II)V
。
作为旁注,您真的应该验证返回值并在每次JNI调用之后检查异常。