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

我正在尝试写我自己的jni资源。 看一些ndk样本,我发现他们经常使用这些macros的JNIEXPORT和JNICALL这样的java包的名称follywed像这样

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

我GOOGLE了,但我不明白何时以及如何使用这些macros

Solutions Collecting From Web of "何时在Android NDK中使用JNIEXPORT和JNICALL?"

JNIEXPORT和JNICALL在NDK_ROOT / platforms / android-9 / arch-arm / usr / include / jni.h中定义。 根据你的设置,这个path将会不同,但是大部分是相似的。

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

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

值得注意的是,默认情况下,所有函数最终都会在dynamic表中,所以任何人都可以很容易地反编译你的本地代码。 每个函数调用都内置在二进制文件中,以防JNI需要find它。 这可以使用编译器选项-fvisibility进行更改。 我build议每个人都设置这个到-fvisibility=hidden来保持你的代码的安全,然后使用JNIEXPORT来标记具有外部可见性的函数。

使用strip命令只是删除debugging符号,dynamic表是分开的。 用objdump玩一下,看看有多less人可以从你的.so文件中解脱出来。

我们最近被这个绊倒了,希望这可以帮助别人。

编辑:我们使用自定义生成系统,所以可见性选项可能会被默认设置为其他生成设置。 更多的信息可以在这个答案中find 。

你可以在你的JNI的机器相关部分find这些macros的定义(通常在$JAVA_HOME/include/<arch>/jni-md.h )。

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

JNICALL包含任何编译器指令,以确保使用适当的调用约定来处理给定的函数。 也许在android上也是空的(这是w32上的__stdcall)。

一般来说,即使它们是空的#define s,你也应该把它们放在里面。

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

简单来说:

  • JNIEXPORT如果你应该使用registerNatives家族的function,那么你不应该使用JNIEXPORT。 否则,你必须使用它。
  • 必须始终使用JNICALL

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

registerNatives允许你在JNI_onLoad或者将来的某个时候以编程方式将function链接起来。 registerNatives允许更早地捕获错误的函数名称,我推荐这个路线。