JNI入门学习笔记(测试环境:ubuntu gcc 4.4.5)

来源:互联网 发布:阿里云 自建数据库 编辑:程序博客网 时间:2024/05/16 04:33

参考文章:

http://blog.csdn.net/yuleslie/article/details/7056577

java native specification 官方文档。

深入理解android:卷1


我这里尝试写一个在ubuntu上直接运行的java调用jni程序,通过这个慢慢入门,然后提高吧。

1. 编辑一个java文件HelloJNI.java(名字随意),这个java文件会去调用jni的方法。

class HelloWorld{private native void print();public static void main(String[] args){new HelloWorld().print();}static{System.loadLibrary("MyJNI");}}

不需要任何头文件包含。代码中看到有个main函数,另外main函数会调用native的print方法。

System.loadLibrary("MyJNI")是要我们要加载的JNI库,实际加载过程中会找这个拓展后的文件名,Linux下为libMyJNI.so,windows下为MyJNI.dll

接下来我们需要去建立我们的MyJNI库,并要保证我们调用native的print函数时能正常调用到JNI库中对应的XXX_print函数,所谓“注册JNI函数”。

2. 编写注册JNI函数

为保证我们JNI库中的函数与java中调用的print对应,我们需要完成JNI函数的注册,相当与映射的意思吧。

这里我们先测试使用简单的静态注册方法,为省去要按JNI规范手动编写代码的困难,我们使用javah来生成头文件。

先将上面的HelloJNI.java用javac编译成 .class文件

tongjiang@tongjiang-laptop:/home/workdir/source/test$ javac HelloJNI.java

此时目录下会根据你HelloJNI.java中定义的HelloWorld类生成一个文件:HelloWorld.class

我们此时使用javah来生成.h文件

tongjiang@tongjiang-laptop:/home/workdir/source/test$ javah HelloWorld

这样会生成HelloWorld.h 头文件,内容如下

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class HelloWorld */#ifndef _Included_HelloWorld#define _Included_HelloWorld#ifdef __cplusplusextern "C" {#endif/* * Class:     HelloWorld * Method:    print * Signature: ()V */JNIEXPORT void JNICALL Java_HelloWorld_print  (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif

我们如果自己手动coding的话就要按JNI规范来写,JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *env, jobject)

我们在jni库中要编写的print函数的原型就是这样了,print被扩展为Java_HelloWorld_print,

静态注册时虚拟机会按这种扩展方法来找jni函数。

另外还有搞怪的,如果我们java文件中print函数是写成有下划线的,假如是print_me,

扩展时需要写成JNIEXPORT void JNICALL Java_HelloWorld_print_lme(JNIEnv *env, jobject)

注意me前面的那个l,另外如果print函数有传参进来时,JNI对应的函数原型的参数需要按JNI的数据类型进行转换,这个是后话。

3. 我们按.h文件的函数原型来写native的c++代码:hello.cpp

#include <jni.h>#include <stdio.h>#include "HelloWorld.h"JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *env, jobject obj){printf("Hello, i am Native\n");return ;}

单纯打印一句话: Hello, i am Native

我们来编译一下这个cpp文件

tongjiang@tongjiang-laptop:/home/workdir/source/test$ g++ -shared -I '/home/tongjiang/Program/jdk1.6.0_25/include' -I'/home/tongjiang/Program/jdk1.6.0_25/include/linux' '/home/workdir/source/test/hello.cpp' -o libMyJNI.so

-shared会把cpp编译成动态库

-o 输出的目标文件,我们这里是动态库libMyJNI.so

-I 是include头文件的目录 jni.h在jdk1.6.0_25/include'中,而jni.h又包含的头文件在jdk1.6.0_25/include/linux中.

4. 我们可以去尝试java调用jni了

我们运行java HelloWorld

tongjiang@tongjiang-laptop:/home/workdir/source/test$ java HelloWorldException in thread "main" java.lang.UnsatisfiedLinkError: no MyJNI in java.library.pathat java.lang.ClassLoader.loadLibrary(ClassLoader.java:1738)at java.lang.Runtime.loadLibrary0(Runtime.java:823)at java.lang.System.loadLibrary(System.java:1028)at HelloWorld.<clinit>(HelloJNI.java:10)Could not find the main class: HelloWorld.  Program will exit.

提示找不到这个库no MyJNI in java.library.path,因为System.loadLibrary会到系统库中找对应的so,但我们的so没有放到系统的lib目录中。

我们这里可以配置export LD_LIBRARY_PATH=/home/workdir/source/test

这样的话会链接LD_LIBRARY_PATH这个目录下的对应so

再次运行java HelloWorld

tongjiang@tongjiang-laptop:/home/workdir/source/test$ java HelloWorld

Hello, i am Native

也可以不export LD_LIBRARY_PATH

执行下面的命令也ok

tongjiang@tongjiang-laptop:/home/workdir/source/test$ java -Djava.library.path=./ HelloWorld 
Hello, i am Native


好,现在我们基本通路已经打通了,java能通过jni调用native层的的函数,我们再尝试函数中传递参数和native调用java层的方法。