jni 全名是java native interface。
JNI资料
什么时候使用JNI:
1、 java api 不能满足程序需要的时候
2、要求性能比较高的时候,算法的计算,图像的渲染等。
3、当需要访问已有的本地库的时候
开发步骤:
1、编写native方法。
2、javah生成 .h 头文件
3、复制 .h文件到cpp目录。
4、实现.h头文件中的声明函数。
1、写个hello world:
C代码:
java代码:
C 中env是一个结构体二级指针。
上面是静态的java方法,现在用一个非静态的方法:
java代码:
C代码:
静态方法和非静态方法除了调用的时候不一样之外在C代码里面只有jclass和jobject这一个参数的不同。
JNI的基本数据类型:
jboolean、jbyte、jchar、jshort、jint、jlong、jfloat、jdouble
引用类型:字符 jstring 其他都是object;
数组:jByteArray 公式:jTypeArray;
引用类型数组:都是jobjectArray;
java方法的签名
如:void a(int b) 签名就是"(I)V"
String gets() 签名是"()Ljava/lang/String;"
其他类型如Bitmap 就是"Ljava/lang/Object"
对于数组,其为 : [ + 其类型的域描述符 + ;
int[ ] 其描述符为[I
float[ ] 其描述符为[F
String[ ] 其描述符为[Ljava/lang/String;
Object[ ]类型的域描述符为[Ljava/lang/Object;
多维数组则是 n个[ +该类型的域描述符 , N代表的是几维数组。例如:
int [ ][ ] 其描述符为[[I
float[ ][ ] 其描述符为[[F
2、c访问java中的非静态域:
C的代码:
java的代码:
显示结果:miK简单的JNI。
错误一:不要直接strcat(text,c_str),因为text有存储“简单JNI”这几个字符的空间,后面拼接的字符存不下,可以使用数组 char[50] text。
错误二的写法:
显示结果:miK
new 了一个mainActivity的新对象 修改了这个对象中的key值,并没有修改当前mainactivity中的key值。
3、c访问java中的静态域:
C代码:
java 代码:
输出结果:11。
也就是C方法上多了一个static。
4、c调用java的非静态方法:
C代码:
java代码:
输出结果就是随机的整数。
5、c调用java的静态方法:
c代码:
java代码:
输出结果就是一串字符串。
6、c调用java的构造方法:
c代码:
java代码:
输出结果:
7、JNI字符串乱码问题:
java使用utf-16的编码方式(16bit),中英文都是用2个字节
jni中使用utf-8的编码方式,英文是一个字节,英文三个字节
C/C++英文使用ASCII编码方式,中文使用GB2312编码方式,两个字节表示一个汉字。
android studio 右下角默认utf-8的编码方式,所以没有EC上的乱码
8、GetStringUTFChars(env, j_str,NULL)第三个参数的问题说明:
输出的日志:iscp = JNI_TRUE。所以第三个参数是取值,不是传值。java的string在是否内存中复制了一份给C使用,可以通过这个判断是否回收释放。
9、访问java基本类型的数组:
c代码:
java代码:
输出结果:
10、访问java引用类型数组:
c代码:
java代码:
输出结果:
11、JNI的引用:
局部引用:
全局引用:
弱全局引用:
12、JNI异常处理:
在java中就可以try catch 捕获了
13、JNI缓存:
局部静态变量缓存:
C代码:
java代码:
只打印了一次“javaKey”,如果把static去掉就会打印多次。
存储在静态区,只需定义并初始化一次,之后一直有。
全局静态缓存:
全局使用这个变量只能在定义之后的方法里使用
14、动态注册:
java代码:
FileUtils:
C代码:
JNI_Onload方法由系统调用,具体在哪调用下一篇JNI原理会有。
静态注册:
1、每个class都需要使用javah生成一个头文件,并且生成的名字很长书写不便;
2、初次调用时需要依据名字搜索对应的JNI层函数来建立关联关系,会影响运行效率
3、用javah 生成头文件方便简单
动态注册:
1、使用一种数据结构JNINativeMethod来记录java native函数和JNI函数的对应关系
2、移植方便(一个java文件中有多个native方法,java文件的包名更换后)