一、关于JNI
JNI( Java Native Interface )主要是实现Java和C/C++语言之间的通信。
Java通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式)。通过调用本地的库文件的内部方法,使Java可以实现和本地机器的紧密联系,调用系统级的各接口方法。
二、实现步骤
(1)把Java中需要的调用的方法加上native关键字,封装到一个类里边。例如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //文件:Ctest.java 2 public class Ctest{ 3 static 4 { 5 System.loadLibrary("myself"); 6 } 7 public native void testJNI(); //声明 8 public static void main(String[] args) 9 { 10 Ctest test=new Ctest (); 11 test. testJNI (); 12 } 13 }
注意:全局类要用类名来定义文件名
(2)使用javac Ctest.java编译代码,生成对应的类文件Ctest.class。
(3)使用javah Ctest生成Ctest.h文件,javah后边跟的是类名字,.h文件里边就是使用jni规则定义的C语言与Java的接口。内容如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* DO NOT EDIT THIS FILE - it is machine generated */ 2 #include <jni.h> 3 /* Header for class Ctest */ 4 5 #ifndef _Included_Ctest 6 #define _Included_Ctest 7 #ifdef __cplusplus 8 extern "C" { 9 #endif 10 /* 11 * Class: Ctest 12 * Method: testJNI 13 * Signature: ()V 14 */ 15 JNIEXPORT void JNICALL Java_Ctest_testJNI(JNIEnv *, jobject); 16 17 #ifdef __cplusplus 18 } 19 #endif 20 #endif
(4)根据Ctest.h中函数的声明来实现函数的定义,编写xxx.c文件,把Ctest.h包含进去即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include "Ctest.h" 3 JNIEXPORT void JNICALL Java_Ctest_testJNI(JNIEnv *env, jobject obj) 4 { 5 printf(" testing jni ............. "); 6 printf("......................... "); 7 }
(5)编译c文件,生成动态链接库.so文件
寻找jni.h和jni_md.h头文件路径:find / -name “*jni.h*”
Ubuntu12.04系统上:/usr/lib/jvm/java-6-openjdk-amd64/include/ 和
/usr/lib/jvm/java-6-openjdk-amd64/include/linux/
树莓派系统上:/usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/include/ 和
/usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/include/linux/
Makefile内容:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 libmyself.so:myself.o 2 gcc -Wall -rdynamic -shared -o libmyself.so myself.o 3 %.o:%.c 4 gcc -I/usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/include/ -I/usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/include/linux/ -fPIC -c -o $@ $^ 5 clean: 6 rm -rf *.o *.so
(6)测试结果:完成
三、问题解决
问1.什么是JDK?如何选择版本?
答1. (Java Development Kit) Java语言的软件开发工具包,主要用来编译Java程序;版本选择:
①SE(J2SE),standard edition,标准版,是我们通常用的一个版本,从JDK 5.0开始,改名为Java SE。
②EE(J2EE),enterprise edition,企业版,使用这种JDK开发J2EE应用程序,从JDK 5.0开始,改名为Java EE。
③ME(J2ME),micro edition,主要用于移动设备、嵌入式设备上的java应用程序,从JDK 5.0开始,改名为Java ME。
问2.JDK的组成有哪些?
答2. javac – 编译器,将源程序转成字节码
jar – 打包工具,将相关的类文件打包成一个文件
javadoc – 文档生成器,从源码注释中提取文档
jdb – debugger,查错工具
java – 运行编译后的java程序(.class后缀的)
appletviewer:小程序浏览器,一种执行HTML文件上的Java小程序的Java浏览器。
Javah:产生可以调用Java过程的C过程,或建立能被Java程序调用的C过程的头文件。
Javap:Java反汇编器,显示编译类文件中的可访问功能和数据,同时显示字节代码含义。
Jconsole: Java进行系统调试和监控的工具
问3.Java和C语言之间的基本数据类型是如何进行转换的?
答3.参考这篇博文http://blog.csdn.net/xyang81/article/details/42047899
问4.Java是如何读C的数组的?又是如何把值写到C语言的数组里?
答4.通过jni.h头文件中的函数来实现数据的传递。例如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //file:testJNI.java 2 import java.util.Arrays; 3 public class testJNI{ 4 static 5 { 6 System.loadLibrary("myself"); 7 } 8 private byte[] mybuf; 9 10 public native void setBuffer(byte[] buffer,int len); 11 public native byte[] getBuffer(); 12 public testJNI() 13 { 14 mybuf = new byte[30]; 15 } 16 public static void main(String[] args) 17 { 18 int i; 19 byte a; 20 byte[] pbuf; 21 a = 'e'; 22 testJNI test=new testJNI(); 23 for(i=0;i<10;i++) 24 test.mybuf[i] = a; 25 test.setBuffer(test.mybuf,10); 26 pbuf = test.getBuffer(); 27 for(i=0; i<10; i++) 28 System.out.print(pbuf[i]+","); 29 System.out.print(' '); 30 } 31 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* DO NOT EDIT THIS FILE - it is machine generated */ 2 #include <jni.h> 3 /* Header for class testJNI */ 4 5 #ifndef _Included_testJNI 6 #define _Included_testJNI 7 #ifdef __cplusplus 8 extern "C" { 9 #endif 10 /* 11 * Class: testJNI 12 * Method: setBuffer 13 * Signature: ([BI)V 14 */ 15 JNIEXPORT void JNICALL Java_testJNI_setBuffer(JNIEnv *, jobject, jbyteArray, jint); 16 17 /* 18 * Class: testJNI 19 * Method: getBuffer 20 * Signature: ()[B 21 */ 22 JNIEXPORT jbyteArray JNICALL Java_testJNI_getBuffer(JNIEnv *, jobject); 23 24 #ifdef __cplusplus 25 } 26 #endif 27 #endif
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include "testJNI.h" 3 4 JNIEXPORT void JNICALL Java_testJNI_setBuffer(JNIEnv *env, jobject obj, jbyteArray buffer, jint len) 5 { 6 int i; 7 char table[30]; 8 char *ptab; 9 printf(" testing jni array............. "); 10 11 //这个函数是将java里的数组拷贝到C这边,执行完table获得java传过来的数组值 12 (*env)->GetByteArrayRegion(env,buffer,0,len,table); 13 for(i=0;i<len;i++) 14 printf("table[%d]=%c,",i,table[i]); 15 printf(" "); 16 /* 17 * 也可以使用这个函数,将本地的指针ptab直接指向Java端的数组地址, 18 * 其实本质上是JVM在堆上分配的这个数组对象上增加一个引用计数,保证 19 * 垃圾回收的时候不要释放,从而交给本地的指针使用,使用完毕后指针 20 * 一定要记得通过ReleaseByteArrayElements进行释放,否则会产生内存泄露。 21 */ 22 ptab = (*env)->GetByteArrayElements(env,buffer,0); 23 if(ptab ==NULL) 24 { 25 printf("ReleaseByteArrayElements error. "); 26 return ; 27 } 28 for(i=0;i<len;i++) 29 printf("ptab[%d]=%c,",i,ptab[i]); 30 printf(" "); 31 (*env)->ReleaseByteArrayElements(env,buffer,ptab,0); 32 } 33 34 JNIEXPORT jbyteArray JNICALL Java_testJNI_getBuffer(JNIEnv *env, jobject obj) 35 { 36 int i; 37 char buffer[30]; 38 for(i=0;i<10;i++) 39 buffer[i] = 'a'; 40 // 在JNI层分配数组空间 41 jbyteArray array = (*env)->NewByteArray(env,30); 42 // 将buffer值复制给JNI层数组 43 (*env)->SetByteArrayRegion(env,array,0,10,buffer); 44 // 返回访问指针 45 return array; 46 // 此外还有一种方法可以实现,使用GetDirectBufferAddress()函数 47 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 libmyself.so:myself.o 2 gcc -Wall -rdynamic -shared -o libmyself.so myself.o 3 4 %.o:%.c 5 gcc -I/usr/lib/jvm/java-6-openjdk-amd64/include/ -I/usr/lib/jvm/java-6-openjdk-amd64/include/linux/ -fPIC -c -o $@ $^ 6 7 clean: 8 rm -rf *.o *.so