繁体   English   中英

何时在Android NDK中使用JNIEXPORT和JNICALL?

[英]when to use JNIEXPORT and JNICALL in Android NDK?

我正在尝试编写自己的jni源代码。 看一些ndk示例,我发现他们经常使用那些宏JNIEXPORT和JNICALL,这些宏的名字跟java包一样

JNIEXPORT void JNICALL Java_com_example_plasma_PlasmaView_renderPlasma(JNIEnv * env, jobject obj, jobject bitmap, jlong time_ms)

我用Google搜索,但我无法理解何时以及如何使用这些宏

JNIEXPORT和JNICALL在NDK_ROOT / platforms / android-9 / arch-arm / usr / include / jni.h中定义。 根据您的设置,此路径将有所不同,但大多数情况类似。

#define JNIIMPORT
#define JNIEXPORT  __attribute__ ((visibility ("default")))
#define JNICALL

JNIEXPORT用于使本机函数出现在构建二进制文件(* .so文件)的动态表中。 它们可以设置为“隐藏”或“默认”( 此处有更多信息)。 如果这些函数不在动态表中,JNI将无法找到调用它们的函数,因此RegisterNatives调用将在运行时失败。

值得注意的是,默认情况下所有函数都会在动态表中结束,因此任何人都可以非常轻松地反编译您的本机代码。 每个函数调用都内置在二进制文件中,以防JNI需要找到它。 可以使用编译器选项-fvisibility更改此设置。 我建议大家将此设置为-fvisibility=hidden以保证代码安全,然后使用JNIEXPORT将函数标记为具有外部可见性。

使用strip命令只删除调试符号,动态表是分开的。 玩objdump,看看一个人可以从你的.so文件中获取多少。

我们最近被这个绊倒了,希望这有助于某人。

编辑:我们使用自定义构建系统,因此默认情况下可以为其他构建设置设置可见性选项。 本SO答案中提供了更多信息。

您可以在JNI包含的机器相关部分中找到这些宏的定义(通常在$JAVA_HOME/include/<arch>/jni-md.h )。

简而言之, JNIEXPORT包含确保正确导出给定函数所需的任何编译器指令。 在android(以及其他基于linux的系统)上,这将是空的。

JNICALL包含确保使用正确的调用约定处理给定函数所需的任何编译器指令。 在android上也可能是空的(在w32上是__stdcall)。

一般情况下,你应该把它们留在里面,即使它们是空的#define

简单来说:

  • JNIEXPORT如果您应该使用registerNatives系列函数,那么您不应该使用JNIEXPORT。 否则你必须使用它。
  • JNICALL必须始终使用。

JNIEXPORT确保函数在符号表中可见。 JNICALL确保函数使用正确的调用约定。 在Android上JNICALL具有基于体系结构的不同值。 ARM是空的,可能会诱使您不包含它。 但是你必须使用JNICALL。

registerNatives允许您以编程方式在JNI_onLoad或以后的某个时间链接该函数。 registerNatives允许更早地捕获错误的函数名称,我推荐这条路线。

只需在您的本机类上运行'javah'并使用它生成的任何内容。 当你拥有一台可以100%可靠地生产它的工具时,你不需要知道这个的细节。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM