一、关于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)测试结果:完成

java实现函数调用方法 java怎么函数调用_Java

三、问题解决

问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语言之间的基本数据类型是如何进行转换的?

java实现函数调用方法 java怎么函数调用_Java_02

问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