Android上的JNI:使用Integer参数从本机进行回调有效,但使用String或Void无效。为什么?

问题描述 投票:1回答:1

我有一个回调类,用于执行从本机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中使用相应的方法),也会发生同样的情况。

为什么会发生,如何解决?

注意:为清楚起见,我已经忽略了我认为与该问题无关的代码的所有部分,如果缺少一些关键的信息,请告诉我并予以解决。

android c++ kotlin callback java-native-interface
1个回答
0
投票

您传递给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调用之后检查异常。

© www.soinside.com 2019 - 2024. All rights reserved.