android - Android : Callback from native with Integer parameter works, 上的 JNI 与 String 或 Void 它没有。为什么?

标签 android c++ kotlin callback java-native-interface

我有一个回调类,用于从 native C++ 代码到 Kotlin 的回调(不确定 Kotlin/Java 是否在这里有所作为,如果有,是否有相关文档?)。
我有一个带有整数参数的工作回调,我从不同的 native 线程调用它没有问题。现在我想添加第二个发送字符串,但由于某种原因不起作用。

我的回调类实现如下所示:

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

所以现在如果我的 native 代码触发 call()我的回调中的方法,我的 integerCallback()JniBridge.kt用整数正确调用。

但这是我没有得到的:如果我将回调更改为发送字符串,它就不起作用。如果我这样改变它:
// 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 调用后验证返回值并检查异常。

关于android - Android : Callback from native with Integer parameter works, 上的 JNI 与 String 或 Void 它没有。为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61713422/

相关文章:

java - 关于 Android Gallery 控件的一般问题

c++ - Qt 从库中调用 QApplication::exec

c++ - 如何让 CPU 在 1 毫秒内精确忙碌?

java - Gradle、Cucumber 和 Windows 命令长度

android - 当应用程序从后台恢复时自动更改抽屉语言

Android:如何以编程方式使用新项目计数更新应用程序快捷方式图标

android - Robolectric 由于调用存储而从 NullPointerException 失败

android - Activity 没有启动,也没有显示错误

c++ - clang-format 打破 lint 注释

Kotlin Jetpack Compose DragGesture 属性取消我的 View 的滚动