JNI自用手册3 - 拾遗篇
文章目录
JNI使用手册,其三,拾遗篇。
Some Tips
性能技巧
- 缓存常用的Class、FieldID、MethoidID;
- 缓存字段可能就有20+倍的差异(取决具体机型);
- 避免复制整个数组;
- GetTypeArrayRegion()和SetTypeArrayRegion()方法可以避免复制整个数组;
- 用参数列表传递多个字段,比传递一个包含多个字段的对象性能要高;
- 尽管传递一个包含多个字段的对象,更符合面向对象设计,且无需修改签名;
- 尽量减少跨层调用;
- 相比相同层的调用更为耗时;
内存泄漏
Java Heap的内存泄漏
- 大量Java对象同时存在,Java Heap容量扩充到上限;
- 程序错误;
native memory的内存泄漏
- JVM中,Java Heap外的内存空间都是native memory;
- 过多线程被创建;
- Native语言使用不当,没有遵循其内存管理机制;
- 局部引用和全局引用没有正确释放;
调用性能
测试项(1亿次) | 华为荣耀7i | 一加5 |
---|---|---|
Java层调用JNI空方法 | 100501 ms | 56102ms |
JNI层调用Java空方法 | 167847 ms | 44559ms |
Java层调用本层空方法 | 62529 ms | 1118ms |
JNI层调用本层空方法 | 1071 ms | 345ms |
- 似乎一加有做了一些优化,jni回调java层空方法耗时居然更小;
- 前两项耗时可能有4000左右的波动,仅供参考;
- 后两项耗时较为稳定,不同机型差异较为明显;
库文件加载
- 调用native方法之前,需要调用System.loadLibrary()加载动态链接库;
- Linux系统一般后缀为so,Windows系统为dll,系统根据平台拓展成真实的动态库文件名;
- Android系统中,定向查找/data/app/${PackageName}/lib/${cpu平台}/libxxx.so
UTF-8编码
- UTF-16
- Java层的字符编码方式;
- 但Java中和UTF-8沾边的字符串操作,都是使用标准的UTF-8;
- MUTF-8(Modified UTF-8);
\u000
表示为0xc0 0x80
- JNI层的字符编码方式;
- 传递给NewStringUTF的数据必须是MUTF-8;
- GetStringChars不需要拷贝,GetStringUTFChars需要分配并转为MUTF-8;
- Get获取的字符串
jchar*
或jbyte*
,都是指向原始数据类型的C指针,不是局部引用,需要手动释放;
- UTF-8编码
\u000
表示为0x00
- 主要使用于C语言;
版本兼容性
- 指定targetsdk,尽可能高;
- 指定minsdk,最好和NDK中的android-target保持一致;
- NDK中未使用高版本的API,但指定了较高的targetsdk,编译时将链接到较高版本;
- 较高版本的部分API实现存在不一致;
- 如signal.h中的signal函数,使用android-21在低于5.0版本系统无法编译运行,错误是无法找到signal函数;
Some Questions
FindClass调用失败
- 多线程情景中
- 从Java层调用Native层,FindClass获取正常;
- 这种调用会携带栈帧信息,包括该类的Class Loader等;
- 在Native层创建的线程中调用FindClass可能会失败,返回空值;
- 解决方案一,缓存Class Loader;
- 解决方案二,在JNI_OnLoad中初始缓存各个Class;
- 从Java层调用Native层,FindClass获取正常;
JNI_OnLoad()与JNI_OnUnload()
- 当Android平台的VM执行System.loadLibrary(),总是先执行JNI_OnLoad()函数;
- 在Android平台,JNI_OnUnload()函数不会被调用,没错,它不会被调用!!;