一、前言
JNI—Java与底层间的桥梁,Android系统中很多的使用了JNI,小编所在项目中核心模块有相关调用。测试中,也有遇到过JNI层的一些问题。理解JNI的调用实现,对于理解Android的代码有帮助。
二、JNI简介
1、概念介绍
JNI是Java Native Interface的简写,可译作Java原生接口。JNI是Java本地调用接口,所谓的Native是调用C/C++的程序。
2、JNI应用场景
1)调用驱动(Java无法处理):需要用到Java标准平台不具备的依赖于操作系统的特性时。由于操作系统提供的驱动一般都是C接口,Java语言并不具备操作这些驱动的能力;
2)对性能要求比较高或对时间敏感,有必要用更底层的语言:Java可以通过JNI调用C/C++的库,对于计算量比较大,处理数据比较多的模块,Java的效率没有C高,所以希望用C去完成;
3)对于某些功能模块,可能Java和C的效率差不多,已经有现成的,用其他语言已经完成的功能时,需要用Java去直接调用,不用Java重写了。
三、JNI技术的原理
在Java程序中调用C/C++程序是通过JNI技术实现的。JNI技术的原理是,在Java源码中声明一个空的特殊函数,Java虚拟机在碰到这个函数时,会加载C/C++程序库,并定位到相应的函数执行其代码。
1、调用过程示意
2、通常执行步骤
1)编写带有native声明的方法的java类;
2)使用javac命令编译所编写的java类(class文件),然后使用javah+java类名将上步中的class文件生成扩展名为h的头文件;
命令:javah–jni×××
Javah程序统一了Java中的native方法,头文件中的函数名和动态库中的函数实现之间的对应关系。
3)用其他语言(C/C++)实现上述头文件中的函数;生成动态链接库,供Java程序使用;
4)发布Java和动态库;
JNI对于应用本身来说,可以看作一个代理模式。对于开发者来说,需要使用C/C++来实现一个代理程序(JNI程序)来实际操作目标原生函数,Java程序中则是jvm通过加载并调用此JNI程序来间接地调用目标原生函数。
四、JNI调用
JNI技术,它是一种双向的接口,使得开发者可以通过Java代码调用到各种native的库,反之亦然。简单来说,JNI可以实现Java调用C/C++,同时C/C++也可调用Java。
Java访问C/C++
Java如何调用C/C++的呢?在调用之前java是不会关心是否已经实现,只有在调用native方法的时候,去找C生成的动态库,如果找不到动态库,那么native方法就会报错。
java的native函数和C中的函数存在一种映射关系,这个映射关系就是遵循的一种协议或者称为规则。
比如,Framework中AssetManager类的成员函数init是一个JNI函数:
private native final void init();
它是由C++层的函数android_content_AssetManager_init来实现的:
static void android_content_AssetManager_init(JNIEnv*env,jobject obj){…}
在C中的规则就是,包名+类名+方法名,并且中间用下划线分割。
第一个参数env,是JNIEnv对象,该对象代表一个Java虚拟机所运行的环境,通过它可以访问JVM内部的各种对象;
第二个参数jobject是调用该函数的对象,上面的例子指的就是AssetManager对象;每个这样的C函数的参数至少有这两个参数,如果native函数里有多个参数,依次在后面排列,java的数据类型和JNI中的数据类型对应关系。
当java调用native函数时,编译器会向native引擎传递调用者的包名,函数名及参数类型,native引擎根据这些信息决定应该调用具体的的哪个函数。
在android中,native引擎中的AndroidRuntime类提供了一个registerNativeMethods()函数,通过此函数定义java方法和C函数的映射关系。
C/C++访问java
C访问java,监听回调中常有使用。
由于java中的函数在native引擎中并没有直接的函数指针,java函数只能由java引擎去执行,而不是C。所以访问java不能通过指针,只能通过参数接口。
java访问c的时候,把类名,函数名,参数类型传递给native引擎,然后由native引擎处理C函数,同理,C调用java时,也需要把想要访问的类名、函数名、参数传递给java引擎。
五、Android中的*.SDK