JNI初步 -- Hello JNI示例
来源:互联网 发布:mac远程协助windows 编辑:程序博客网 时间:2024/05/16 08:40
由于要在android平台上做一个so动态链接库,所以今天抽空看了一下jni。以前只是听说过java通过jni和C/C++互调,但是由于项目中没有真正用到这项技术,所以也没有抽出时间专门学习。现在正好趁这个机会系统学习一下jni。今天参考某本书,做了一个小的demo(传说中的HelloWorld)。参考书是开发的windows平台中的dll动态库,而我做的是LInux平台上的so动态库。由于之前一直做java,对linux上的GNU环境不是很熟悉,遇到一些问题,也在这篇博客里做一下记录。
首先介绍一下我的开发环境:jdk1.6,Ubuntu 12.04 64bit,gcc 4.6.3
第一步:在我的家目录中创建java文件
在java文件中声明native方法,并且在类加载的时候加载so动态库。这里的java文件没有使用包名。java代码如下:
public class HelloJni{static{System.loadLibrary("hellojni");}public static void main(String[] args){HelloJni helloJni = new HelloJni();helloJni.sayHello();}native void sayHello();}
第二步:编译java文件,生成class文件
执行以下命令
javac HelloJni.java
可以看到当前目录下生成HelloJni.class
zhangjg@MyUbuntu:~$ lsbin HelloJni.class simplegit-progit workspace 视频 下载examples.desktop HelloJni.java ticgit 公共的 图片 音乐HelloJni.c~ HelloJni.java~ Ubuntu One 模板 文档 桌面
第三步:生成头文件
执行javah命令,生成c头文件
javah HelloJni
可以看到当前目录下出现HelloJni.h头文件。
zhangjg@MyUbuntu:~$ lsbin HelloJni.h ticgit 模板 下载examples.desktop HelloJni.java Ubuntu One 视频 音乐HelloJni.c~ HelloJni.java~ workspace 图片 桌面HelloJni.class simplegit-progit 公共的 文档
该文件的全部内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class HelloJni */#ifndef _Included_HelloJni#define _Included_HelloJni#ifdef __cplusplusextern "C" {#endif/* * Class: HelloJni * Method: sayHello * Signature: ()V */JNIEXPORT void JNICALL Java_HelloJni_sayHello (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif
第四步:创建c文件
在当前目录下创建HelloJni.c文件,在这个c文件中包含上一步中创建的HelloJni.h头文件,并且实现头文件中声明的方法,代码如下:
#include <stdio.h>#include "HelloJni.h"/*该方法在HelloJni.h中声明 JNIEXPORT和JNICALL都是JNI中的关键字 JNIEnv对应java线程中调用的JNI环境,通过这个参数可以调用一些JNI函数 jobject对应当前java线程中调用本地方法的对象*/JNIEXPORT void JNICALL Java_HelloJni_sayHello (JNIEnv * env, jobject obj){printf("HelloJni! This is my first jni call.");}
第五步:编译c文件成动态库
执行gcc命令,将c文件编译成动态库,输入以下命令
zhangjg@MyUbuntu:~$ gcc -shared -o hellojni.so HelloJni.c
得到以下错误提示
In file included from HelloJni.c:2:0:HelloJni.h:2:17: 致命错误: jni.h:没有那个文件或目录编译中断。
这是因为在HelloJni.h头文件中又包含了其他jni.h头文件,jni.h中又包括了jni_md.h头文件,这两个头文件在jdk的安装目录中而不在系统的头文件目录中,gcc不知道去哪里找这两个头文件,所以要在gcc的命令中指定这两个头文件所在的路径
执行以下命令:
gcc -fPIC -D_REENTRANT -I/develop/jdk1.6.0_31/include -I//develop/jdk1.6.0_31/include/linux -shared -o hellojni.so HelloJni.c
可以看到当前目录下出现了hellojni.so动态库。
zhangjg@MyUbuntu:~$ lsbin HelloJni.class hellojni.so workspace 图片 桌面examples.desktop HelloJni.h simplegit-progit 公共的 文档HelloJni.c HelloJni.java ticgit 模板 下载HelloJni.c~ HelloJni.java~ Ubuntu One 视频 音乐
第六步:执行class文件,验证jni调用是否成功
执行如下命令:
java HelloJni抛出如下错误:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no hellojni 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 HelloJni.<clinit>(HelloJni.java:4)Could not find the main class: HelloJni. Program will exit.错误提示很明白,意思是说java虚拟机不能在java.library.path路径中找到hellojni动态库。所以,虚拟机在加载动态链接库时,并不是在当前目录下寻找,而是在java.library.path指定的路径下寻找。而java.library.path路径到底是什么呢?可以看出这应该是虚拟机的一个属性,下面编写一段程序,打印出这个路径。代码如下:
public class TestLibraryPath{public static void main(String[] args){String path = System.getProperty("java.library.path");System.out.println(path);}}
执行之后打印出的结果为
/develop/jdk1.6.0_31/jre/lib/amd64/server:/develop/jdk1.6.0_31/jre/lib/amd64:/develop/jdk1.6.0_31/jre/../lib/amd64:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
/develop/jdk1.6.0_31是我的jdk的安装目录。java.library.path指定的共享库目录并不只有一个,而是有多个,之间用:分割。也就是说必须把共享库放到这写目录中的其中一个中,虚拟机才能加载这个动态库。我们把这个动态库放到/develop/jdk1.6.0_31/jre/lib/amd64目录中。执行如下命令:
zhangjg@MyUbuntu:~$ sudo cp hellojni.so /develop/jdk1.6.0_31/jre/lib/amd64/hellojni.so将目录切换到/develop/jdk1.6.0_31/jre/lib/amd64中,可以看到hellojni.so
zhangjg@MyUbuntu:~$ cd /develop/jdk1.6.0_31/jre/lib/amd64zhangjg@MyUbuntu:/develop/jdk1.6.0_31/jre/lib/amd64$ ls | grep hellojni.sohellojni.so再次用java命令执行HelloJni,发现还是报相同的错误,也就是链接错误,找不到要加载的动态库。
修改目录/develop/jdk1.6.0_31/jre/lib/amd64中的hellojni.so的权限试一下
zhangjg@MyUbuntu:/develop/jdk1.6.0_31/jre/lib/amd64$ sudo chmod 777 hellojni.so改变权限后,执行java HelloJni, 还是出现同样的错误。
这就奇怪了, 明明将动态库放入系统指定的目录中,并且增加了所有权限,为什么还是无法加载呢?经过调查得知,linux的动态库命名必须以lib开头,即libXXX.so。XXX是动态库的名字,也就是加载的时候指定名字为XXX而不是libXXX。所以只要将hellojni.so改名为libhellojni.so即可。执行以下命令更改名字:
zhangjg@MyUbuntu:/develop/jdk1.6.0_31/jre/lib/amd64$ sudo mv hellojni.so libhellojni.so
再次执行java HelloJni,打印出c函数中要输出的字符串:
zhangjg@MyUbuntu:~$ java HelloJniHelloJni! This is my first jni call.
到此为止, Linux上的简单的jni调用示例全部完成。
- JNI初步 -- Hello JNI示例
- NDK示例:hello-jni
- ffmpeg 中hello-jni demo 示例
- JNI的初步了解与简单示例
- Hello, JNI.
- Hello JNI
- hello-jni
- Hello Jni
- Hello JNI
- JNI初步
- JNI初步
- JNI初步
- JNI初步
- JNI初步
- JNI示例
- JNI示例
- JNI示例
- JNI之Hello-JNI进阶
- PHP算法 参数组合,多个分类不同组合列表
- 完善ing——输入年月日输出这一年为该年的第多少天
- 避免 C/C++ 程序一闪而过的方法
- 输入年月日求其为本年第几天
- 对栈,堆,静态区的认识
- JNI初步 -- Hello JNI示例
- PHP 日期 加减 月数,天数,周数,小时,分,秒等等
- 搜索工具库Lucence名称的来源,lucence是什么意思?Lucene是Doug妻子的中名 ,同时也是他外祖母的姓
- Map对象用JSON反序列化的方法
- redmine插件列表
- 用 Addr2line 将函数地址解析为函数名
- Spring事务传播特性实例解析
- Calendar类的常规用法
- Linux内核中的双向循环链表学习