从本机 c 代码 (JNI) 为 Java 中的回调函数传递多个参数

Posted

技术标签:

【中文标题】从本机 c 代码 (JNI) 为 Java 中的回调函数传递多个参数【英文标题】:Passing multiple arguments for callback function in java from native c code (JNI) 【发布时间】:2017-11-28 10:14:52 【问题描述】:

以下代码从本地 c 代码调用 java 中的回调函数,传递一些字符串数据作为参数。

原生C层

jmethodID statusId = env->GetMethodID(pctx->jniHelperClz, "CallbackHandler", "(Ljava/lang/String;)V");
jstring string_data = env->NewStringUTF((const char*)"SOME_STRING_DATA");
env->CallVoidMethod(pctx->jniHelperObj, statusId, string_data);
env->DeleteLocalRef(string_data);

android / Java(回调处理程序)

@Keep
private void CallbackHandler(String string_data) 
    // Some Code

除了字符串,我还想传递一个 int 类型的数据。我的 java 回调处理程序如下所示。我应该在我的原生层中进行哪些更改以支持两个参数。

@Keep
private void CallbackHandler(String string_data, int int_data) 
    // Some Code

【问题讨论】:

所以在调用CallVoidMethod时添加jint参数——它的签名是:void CallVoidMethod(jobject obj, jmethodID methodID, ...) 您需要编辑函数指针CallVoidMethod 的参数以在结构env 中多出一个int GetMethodID 的最后一个参数,即 ("(Ljava/lang/String;)V") 也必须更改对吗? 正确:签名也必须更改 - 使用 javap 打印出来 【参考方案1】:

您需要将方法签名从(Ljava/lang/String;)V 更改为(Ljava/lang/String;I)V

jmethodID statusId = env->GetMethodID(pctx->jniHelperClz, “CallbackHandler”, "(Ljava/lang/String;I)V”);

另外,您使用DeleteLocalRef() 的方式不正确。此方法用于删除通过NewLocalRef() 创建的本地引用,但NewStringUTF() 不会创建它们。方法NewStringUTF() 在垃圾收集器控制的Java 堆中创建jstring 对象。您无需手动删除。

注意:

本地引用在本地方法调用期间有效。在本机方法返回后,它们会自动释放。每个本地引用都会消耗一定数量的 Java 虚拟机资源。程序员需要确保本地方法不会过度分配本地引用。虽然本地方法返回 Java 后会自动释放本地引用,但过多分配本地引用可能会导致 VM 在本地方法执行期间耗尽内存。

您需要使用DeleteLocalRef() 来立即删除大对象(例如在循环中)。

【讨论】:

“它们在本机方法返回后自动释放” OP 是从本机代码调用 Java,而不是相反。如果本地方法没有返回 Java(或者如果/当本地线程与 VM 分离时),本地引用将不会被自动删除。 有趣的想法,@Michael。我可以在哪里阅读更多相关信息?可能你有代码示例的链接吗? 我不确定是否有任何好的文档来描述这一点。但是您可以查看 Android 的运行时实现以了解在不同情况下会发生什么,例如android.googlesource.com/platform/dalvik.git/+/… 中的 Jni.cpp 和 Thread.cpp(是的,现在 Android 使用 Art 而不是 Dalvik,但 Dalvik 源代码通常更易于浏览)。 感谢您的链接,@Michael。我没想到你建议阅读android平台的完整源代码:) 谢谢@Sergey。下面的代码对我有用。 methodID statusId = env->GetMethodID(pctx->jniHelperClz, “CallbackHandler”, "(Ljava/lang/String;I)V”);jstring string_data = env->NewStringUTF((const char*)"SOME_STRING_DATA");jint int_data = 10; // some numberenv->CallVoidMethod(pctx->jniHelperObj, statusId, string_data, int_data);env->DeleteLocalRef(string_data);

以上是关于从本机 c 代码 (JNI) 为 Java 中的回调函数传递多个参数的主要内容,如果未能解决你的问题,请参考以下文章

JNI维数组:将二维数组从java传递给c

如何跟踪从Java到C的JNI方法?

使用 JNI 在 Java 调用之间将本机对象保存在内存中

如何从java本机接口调用getStackTrace方法(jni)

Android NDK将参数传递给本机方法

JNI将参数传递给c ++的方法