一、JNI介绍
·JNI(Java Native Interface)是一种编程框架,允许 Java 代码与用其他编程语言(如 C 或 C++)编写的代码进行交互。JNI 是 Java 语言的一个重要特性,它使得 Java 程序能够调用和被 C/C++ 程序调用,从而充分利用 Java 的跨平台特性和 C/C++ 的高性能特性。
·JNI 的主要用途
1、访问本地资源:访问操作系统级别的资源,如文件系统、网络接口等。
2、调用高性能代码:调用用 C/C++ 编写的高性能代码,如数学计算、图形处理等。
3、与现有系统集成:与现有的 C/C++ 库或系统进行集成,避免重新编写代码。
4、访问硬件设备:直接访问硬件设备,如摄像头、传感器等。
·JNI 允许 Java 代码调用本地方法(用 C/C++ 编写的函数),并允许本地代码访问 Java 对象和调用 Java 方法。JNI 的工作原理可以分为以下几个步骤:
1、声明本地方法:在 Java 类中声明本地方法。
2、生成头文件:使用 javac 工具生成 JNI 头文件。
3、实现本地方法:在 C/C++ 中实现本地方法。
4、编译本地代码:将 C/C++ 代码编译为共享库(如 .dll、.so 文件)。
5、加载共享库:在 Java 程序中加载共享库并调用本地方法。
二、java中声明的本地接口转换成jni头文件中声明的函数
·为什么要转换
1、数据类型不匹配问题:jave的数据类型占用空间大小,和c语言数据类型占用空间大小不一样,比如Int类型,无论什么平台都是4字节,而在c语言中,32位平台占用4字节,64位平台占用8字节
2、因为java中有重载和重写,导致java中的函数和c语言中的函数不匹配的问题
重载:方法名相同,参数列表不同
重写:方法名相同,参数列表也相同
对于重载,java如果直接根据函数名调用c实现的接口,可能会存在参数不匹配的问题,因为还需要根据参数来调用c实现的接口
·数据类型转换
java数据类型转换成jni类型

·JAVA方法转换成JNI签名步骤
方法转换成签名(jni),为了给方法加标签,使得重载函数签名后能唯一对应c语言实现的接口
1、参数,返回值转换成签名
例子:
java: init test(int a, int b)
jni数据类型转换:I test(I a, I b)
java: init test(int a, int b[])
jni数据类型转换:I test(I a, [I b)
2、整个方法转换成签名
例子1:
java: init test(int a, int b[])
jni数据类型转换:I test(I a, [I b)
签名:(I[I)I(标签,标签添加到函数名之后,组合一起使得java方法和c中函数一一对应)
例子2:
string test(int a, string b[])
去掉方法名:string (int a, string b[])
jni数据类型转换:(I[Ljava/long/string)Ljava/long/string;
三 、java方法调用c函数接口的步骤(JNI的静态注册)
1、标记方法:在方法名前加native标识,通知虚拟机DVM该方法不在java中实现,而是在c中实现
2、使用system.loadlibrary函数
system.loadlibrary“库名”,在c库中根据库名找到对应的库,在库中找到方法对应的c语言实现函数
库是c语言实现的,编译成库名为 libxxx.so的库
四、java方法调用c函数接口操作示例
1、准备工具:安装JDK
javc命令,编译java源码,将.java文件编译成.class文件
javah命令,将.class文件生成jni头文件
2、创建一个java源码
1)声明本地方法:在 Java 类中声明本地方法
2)system.loadlibrary加载本地库代码
public class HelloWorld {
static { //static是静态代码块,类加载的时候只执行一次
System.loadLibrary("helloworld");
}
public native void hello(); (hello是HelloWorld 类中的一个方法)
public static void main(String[] args) {
new HelloWorld().hello();
}
}
3. 生成头文件:使用 javac 工具生成 JNI 头文件
1)编译:javac HelloWorld.java
2)生成JNI头文件javah -jni HelloWorld
将生成一个名为 HelloWorld.h 的头文件。这就是JNI 头文件,包含了c中要实现的代码
4. 实现本地方法:在 C 中实现以上jni中声明的本地要实现的方法
#include
#include
#include "HelloWorld.h" (jni头文件)
JNIEXPORT void JNICALL Java_HelloWorld_hello(JNIEnv *env, jobject obj) {
printf("Hello from C!\n");
}
5. 编译本地代码:将 C 代码编译为共享库:
// 在 Linux 上
gcc -shared -fpic -o libhelloworld.so -I${JAVA_HOME}/include(jni.h的目录) -I${JAVA_HOME}/include/linux HelloWorld.c
// 在 Windows 上
cl /I"%JAVA_HOME%\include" /I"%JAVA_HOME%\include\win32" /LD HelloWorld.c /FeHelloWorld.dll
6. 加载共享库,运行 Java 程序:
export LD_LIBRARY_PATH=.
java HelloWorld
五、JNI的动态注册
1、回顾静态注册
1)实现原理:根据函数名来建立java方法和JNI函数间的一对应关系
2)实现过程
编写java代码
编译java代码,生成.class
通过javah,利用.class文件生成JNI的.h文件(生成后的JNI头文件包含了java函数在JNI层的声明)
使用.c文件实现JNI层声明的函数
2、动态注册
1)实现原理:直接告诉native函数其在JNI中对应函数的指针(Jni_onload);
2)实现过程
1、利用结构体JNINativeMethod保存Java Native函数和JNI函数的对
2、在一个JNINativeMethod数组中保存所有native函数和JNI函数的对应关系;
3、在Java中通过System. loadLibrary加载完JNI动态库之后,调用JNI_OnLoad函数,开始动态注册;
4、JNI_OnLoad中会调用RegisterNatives函数进行本地方法注册;
3、动态注册实现步骤:
1)java实现(同静态注册)
native声明本地函数
system.loadlibrary()加载本地库
2)c实现(不同于静态注册)
实现Jni_onload函数
3)java如何调用c库中的Jni_onload函数,或者c库中的Jni_onload函数怎么知道是哪个类调用了Jni_onload函数
获取JVM,Jni_onload(jvm),安卓系统传参获取
获取类:通过JVM虚拟机的env指针变量的findclass方法找到类,关联c代码和java的类(一个类对应一个java源码)
本地方法映射到,关联java类的方法和c语言代码中的函数,使用结构体JNINativeMethod
9678

被折叠的 条评论
为什么被折叠?



