一、关于JNI
JNI( Java Native Interface )主要是实现Java和C/C++语言之间的通信。
Java通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式)。通过调用本地的库文件的内部方法,使Java可以实现和本地机器的紧密联系,调用系统级的各接口方法。
二、实现步骤
(1)把Java中需要的调用的方法加上native关键字,封装到一个类里边。例如:
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的接口。内容如下:
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包含进去即可。
1 #include <stdio.h>
2 #include "Ctest.h"
3 JNIEXPORT void JNICALL Java_Ctest_testJNI(JNIEnv *env, jobject obj)
4 {
5 printf("\ntesting jni .............\n");
6 printf(".........................\n");
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内容:
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语言之间的基本数据类型是如何进行转换的?
问4.Java是如何读C的数组的?又是如何把值写到C语言的数组里?
答4.通过jni.h头文件中的函数来实现数据的传递。例如:
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('\n');
30 }
31 }
testJNI.java
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
testJNI.java
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("\ntesting jni array.............\n");
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("\n");
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.\n");
26 return ;
27 }
28 for(i=0;i<len;i++)
29 printf("ptab[%d]=%c,",i,ptab[i]);
30 printf("\n");
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 }
myself.c
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
Makefile