面试题十三:Java Native方法与Native函数是怎么绑定的?
Java Native方法与底层Native函数是怎么绑定的?
一、面试官视角:这道题想考察什么?
是否有Native开发经验
Java Native方法只是声明,没有实现,底层Native函数才是实现。
是否面对知识善于发现背后的原因
二、题目剖析
1、静态绑定:通过命名规则映射
1 |
|
extern “C”的用处?
它是用来告诉编译器,编译这个底层Native函数的时候一定要按照C的规则保留名字(Java_io_github_apkok_nativec_NativeCInf_callNativeStatic),不能去混淆,因为它是直接将名字写到符号表里面的,不然你混淆了,我就找不着了,因此并不是你按照命名规则定义好了底层Native函数名称就好了,而是要看符号表具体存的是什么名称,才能够映射到Java的Native方法。
JNIEXPORT的用处?
上一篇文章,我们提到过优化so库的时候,尽可能的让它隐藏,因为不是所有的Native函数写出来都一定要在符号表里面出现的,但JNIEXPORT就是让Native方法强制扔到DEFAULT里面,这样的话,我们就可以在符号表里面看到。
JNICALL的用处?
1
2
#define JNIEXPORT _attribute_((visibility("default")))
#define JNICALL我们看到#define JNICALL定义是空的,后面啥也没有,那定义它干嘛,其实并不是没用哟,因为有些平台,比如在mips上编译的时候就会有定义,还有在windows上编译的时候会定义为sddcall,告诉编译器,函数调用的惯例是什么,比如参数是怎么入栈的,函数返回了这个栈谁来清理等等,是用来约束这些的,所以JNI并不一定编译在Android系统上,windows上也是可以的,因此JNICALL主要是考虑兼容性的问题。
2、动态绑定:通过JNI函数注册
1 |
|
- 动态绑定可以在任何时刻触发
- 动态绑定之前根据静态规则查找Native函数
- 动态绑定可以在绑定后的任意时刻取消
3、动态和静态绑定对比
动态绑定 | 静态绑定 | |
---|---|---|
Native函数名 | 无要求 | 按照固有规则编写且采用C的名称修饰规则 |
Native函数可见性 | 无要求(会减小so库大小) | 可见(会占用符号表的大小) |
动态更换 | 可以 | 不可以 |
调用性能 | 无需查找 | 有额外的查找开销(查找符号表) |
开发体验 | 几乎无副作用 | 重构代码时较为繁琐 |
Android Studio支持 | 不能自动关联跳转 | 自动关联JNI函数可跳转 |
三、题目结论
- Native方法绑定主要有静态和动态两种
- 静态绑定注意so库的符号表以及函数的名称修饰
- 动态绑定注意与静态绑定的互补关系以及调用时机
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!