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

naw*_*ara 26 c++ macros java-native-interface android 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搜索,但我无法理解何时以及如何使用这些宏

Dan*_*ker 35

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

#define JNIIMPORT
#define JNIEXPORT  __attribute__ ((visibility ("default")))
#define JNICALL
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

  • JNICALL是一个关于android的空定义(如上所述),但它是用于平台兼容性的.在Windows上JDKs JNICALL定义为`__stdcall`(https://msdn.microsoft.com/en-us/library/zxk0tw93.aspx) (2认同)

tec*_*age 6

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

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

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

一般来说,你应该把它们留在里面,即使它们是空#define的.

  • 在Android上的某些架构上,JNICALL并不是空的.应始终使用JNICALL.如果您不使用registerNatives函数系列,则应使用JNIEXPORT. (2认同)

ove*_*tic 5

简单来说:

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

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

registerNativesJNI_onLoad也允许您在将来或将来以编程方式链接该函数。registerNatives允许更早地捕获错误的函数名称,我推荐这条路线。