JNI的使用(一)

来源:互联网 发布:财务尽调 知乎 编辑:程序博客网 时间:2024/05/21 10:24

   Java Native Interface (JNI)标准是java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI 是本地编程接口,它使得在 Java 虚拟机 (VM) 内部运行的 Java 代码能够与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行交互操作。

关于Android的JNI知识可以参考这篇文章:http://blog.csdn.net/linweig/article/details/5417319

在java里调用无参无返回值的c函数:

JNIDemo.java代码如下:

public class JNIDemo{static{/*1. load library in static code block*/System.loadLibrary("native"); // it will general the libnative.so when compile the file}public native static void hello(); //decalare the native static function public static void main(String args[]){/*2. java (hello) <------> c (c_hello)  map on c file*//*3.call*/hello();}}
java调用c函数分为三步:

1.加载library

2.建立映射关系 (在C函数里面映射)

3.调用

注意:

我们一般在静态代码块里使用System.loadLibrary加载library,参数是我们加载的c函数文件名。在执行时会首先加载由native.c编译的libnative.so文件;由于我们要调用的hello函数是在本地C语言里面实现的,因此在JNIDemo这个类里面要声明本地的静态函数。  

编译:javac JNIDemo.java 

native.c代码如下:

/*1.在这个C语言程序里面写一个c_hello供Java程序调用*//*2.建立C语言和Java的映射关系*/#include <stdio.h>#include <jni.h>  /*/usr/lib/jvm/java-1.7.0-openjdk-amd64/include*//*写一个c_hello (需要加参数),里面打印一句话*/void c_hello(JNIEnv * env , jclass jc){printf("Hello world !\n");}/*定义一个JNINativeMethod数组(可以有多个本地函数),用来表示java要调用的本地c函数*/static const JNINativeMethod methods[]={{"hello","()V" , (void *)c_hello}, //第二个参数表示字段描述符: java里的调用的hello函数没有参数,返回值为空:"()V"};/*在C库里面要实现一个JNI_OnLoad方法,在java程序System.loadLibrary("native");时会调用该方法建立映射关系*/JNIEXPORT jint JNICALLJNI_OnLoad(JavaVM *jvm, void *reserved) //参考jni.pdf JNI_OnLoad函数{JNIEnv *env;jclass cls;/*获得运行环境1.4版本*/if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {return JNI_ERR; /* JNI version not supported */}/*查找在java里调用该方法的类名*/cls = (*env)->FindClass(env, "JNIDemo");if (cls == NULL) {return JNI_ERR;}/*调用RegisterNatives建立联系java (hello) <------> c (c_hello)  map on c file*/if((*env)->RegisterNatives(env, cls, methods, 1)<0)//把这个方法注册进环境的类里面{return JNI_ERR;}/*成功,返回JNI版本号*/return JNI_VERSION_1_4; }
在native.c里面,首先实现我们要调用的 c_hello 函数,可以看到它是一个无返回值的c函数。注意到它的参数:JNIEnv * env ,表示运行环境指针,jclass jc 表示JNI的类名。当java里面的JNIDemo 来调用c_hello 时,会通过这两个参数来找到它。

在C库里面要实现一个JNI_OnLoad方法,在java程序System.loadLibrary("native");时会调用该方法建立映射关系。关于这个函数可以参考JNI官方文档,网上也有很多。它首先需要通过java虚拟机获得一个运行环境:(*jvm)->GetEnv,第一个参数是传入的JavaVM *jvm,第二个参数&env 用来存放运行环境,第三个参数为JNI版本号,表示要使用哪个版本的JNI。使用(*env)->FindClass查找在java里调用该方法的类名,这里是JNIDemo 这个类调用该方法。

接下来就是最重要的一步了,建立映射关系。使用(*env)->RegisterNatives建立映射。参数methods表示把哪些方法注册进java类里面,它是一个结构体数组,这就意味着我们可以注册多种方法,即在java里面调用多个c函数。这个methods数组的成员是一个JNINativeMethod 结构体:

typedef struct {char *name; //java里面点用的函数名char *signature;//描述符,用来表示 java里的调用的hello函数的参数和返回值的字段描述符void *fnPtr;//C语言实现的本地函数指针} JNINativeMethod;

这个结构体里面比较重要的是signature成员,它是一个字段描述符。当c程序里面实现多个同名的方法时,可以通过signature来进一步确定调用哪个方法。由于这里调用的c_hello是一个无参数、无返回值的c函数,因此在methods结构体中signature成员是"()V"。关于signature成员如何确定可以参考JNI的官方文档,网上也有详细说明。

编译native.c生成libnative.so:

gcc -I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include -shared -fPIC -o libnative.so native.c
用到了jni.h,指定其在pc机里面的路径:-I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include
生成libnative.so库文件:-shared

修改环境变量LD_LIBRARY_PATH为当前路径:
export LD_LIBRARY_PATH=.

执行:java JNIDemo
Hello ,world!

这里java程序就成功地调用了C程序。

如果要生成JNI头文件JNIDemo.h,使用如下命令:
javah -jni JNIDemo
则生成了一个头文件JNIDemo.h
里面的内容:

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class JNIDemo */#ifndef _Included_JNIDemo#define _Included_JNIDemo#ifdef __cplusplusextern "C" {#endif/* * Class:     JNIDemo * Method:    hello * Signature: ()V */JNIEXPORT void JNICALL Java_JNIDemo_hello  (JNIEnv *, jclass);#ifdef __cplusplus}#endif#endif
可以看到hello方法的 Signature: ()V 描述符。
我们以后可以使用这个命令来生成Signature,这是一个取巧的方法。
注意:这个头文件的生成只取决于JNIDemo.java 里定义的要调用的方法,与native.c中如何实现该方法无关。

0 0
原创粉丝点击