Android JNI学习
JNI是Android中比较重要的一块知识,Java层与C/C++层进行调用的桥梁。所以今天就来学习一下JNI相关的知识。
JNI(Java Native Interface)
JNI(Java Native Interface):java本地开发接口,JNI是一个协议,这个协议用来沟通java代码和外部的本地代码(c/c++),外部的c/c++代码也可以调用java代码。
####为什么使用JNI?
- 效率上 C/C++是本地语言,比java更高效
- 代码移植,如果之前用C语言开发过模块,可以复用已经存在的c代码
- java反编译比C语言容易,一般加密算法都是用C语言编写,不容易被反编译
Java基本数据类型与C语言基本数据类型的对应
引用类型对应
JNI例子
在AS3.0之后,AS对JNI的工程构建做了一些改动,可以非常方便地创建一个支持JNI的工程。
只要在创建工程的时候选择包括C++,其他都是正常的创建流程。
最终创建出的工程结构如下图:
默认生成一个stringFromJNI
的方法,返回值为String类型。
生成的对应C代码为:
1 |
|
AS帮我们创建一个CPP文件夹用来保存我们的C/C++文件,CMakeList文件也帮我们创建成功,可以直接运行,极大方便了我们Android开发程序员。
C/C++中生成方法的名称规范为:Java _ 包名 _ 类名 _ 方法名
JNI传递一个数组
上面AS自动生成的示例代码展示了JNI对String的操作,然后我们看一下JNI传递一个数组。
Java代码
1 | public native void change(int[] arr); |
生成C/C++代码
在AS3.0之后也不用我们去自己写对应的C/C++代码,AS可以帮我们自动生成。
生成的模版代码:
1 | extern "C" JNIEXPORT void |
自动生成的代码连获取数组数据以及释放数组内存都帮我们写好了,简直了~
然后我们做一些操作:
1 | extern "C" JNIEXPORT void |
在Java中调用该方法
1 | int[] a = {1, 2, 3, 4, 5, 6}; |
打印结果:
1 | E/Test: a0= 11 |
每个数据都加了10,OK。
可以看到我们在这个方法中没有返回值,直接打印相同的数组,但实际结果也发生了改变,这是因为:传递数组其实是传递一个堆内存的数组首地址的引用过去,所以实际操作的是同一块内存,当调用完方法,不需要返回值,实际上参数内容已经改变,Android中很多操作硬件的方法都是这种C语言的传引用的思路。
在C++中调用java方法
Java中:
1 | public void sayJavaHi() { |
我们调用say方法,然后由C调用sayJavaHi方法。
C++方法:
1 |
|
在C++方法中我们使用反射调用Java层的代码。
最终结果:
1 | 08-17 20:30:05.637 8791-8791/com.liuwei.ndktest D/System.out: debug日志 |
补充
这里说一下GetMethodID方法,第一个参数:Java类对象;第二个参数:参数名(或方法名);第三个参数:该参数(或方法)的签名。
比较麻烦的是第三个参数,JNI是以”(*)+”形式表示函数的有哪些传入参数,传入参数的类型,返回值的类型。”()” 中的字符表示传入参数,后面的则代表返回值。
例如:
“()V” 就表示void Func();
“(II)V” 表示 void Func(int, int);
“(Ljava/lang/String;Ljava/lang/String;)I”.表示 int Func(String,String)
另外数组类型的简写,则用”[“加上如表A所示的对应类型的简写形式进行表示就可以了,
比如:[I 表示 int [];[L/java/lang/objects;表示Objects[],另外。引用类型(除基本类型的数组外)的标示最后都有个”;”
补充二
对于这个方法参数中的JNIEnv* env参数的解释:
JNIEnv类型实际上代表了Java环境,通过这个JNIEnv* 指针,就可以对Java端的代码进行操作。例如,创建Java对象,调用Java对象的方法,获取Java对象中的属性等等。JNIEnv的指针会被JNI传入到本地方法的实现函数中来对Java端的代码进行操作。
JNIEnv类中有很多函数可以用:
NewObject:创建Java类中的对象
NewString:创建Java类中的String对象
New
Get
Set
GetStatic
SetStatic
Call
CallStatic
等许多的函数。