【转】JNI(Java Native Interface)的简介
来源:互联网 发布:帝国cms 图片系统模型 编辑:程序博客网 时间:2024/05/17 03:08
因为刚刚接触android和java平台,对jni到底是什么,还不是很了解,所以从csdn转载了一篇文章,以便自己对jni有一个认识,也跟大家分享一下。
jni是java native interface的缩写。从java 1.1开始,java native interface (jni)标准成为java平台的一部分,它允许java代码和其他语言写的代码进行交互。jni一开始是为了本地已编译语言,尤其是c和c++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。
使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。jni标准至少保证本地代码能工作在任何java 虚拟机实现下。
一、jni(java native interface)的设计目的
·the standard java class library may not support the platform-dependent features needed by your application.
·you may already have a library or application written in another programming language and you wish to make it accessible to java applications
·you may want to implement a small portion of time-critical code in a lower-level programming language, such as assembly, and then have your java application call these functions
二、jni(java native interface)的书写步骤
·编写带有native声明的方法的java类
·使用javac命令编译所编写的java类
·使用javah ?jni java类名生成扩展名为h的头文件
·使用c/c++实现本地方法
·将c/c++编写的文件生成动态连接库
1) 编写java程序:
这里以helloworld为例。
代码1:
class helloworld {
public native void displayhelloworld();
static {
system.loadlibrary("hello");
}
public static void main(string[] args) {
new helloworld().displayhelloworld();
}
}
声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明改方法为native的,并且不能实现。其中方法的参数和返回值在后面讲述。
load动态库:system.loadlibrary("hello");加载动态库(我们可以这样理解:我们的方法displayhelloworld()没有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是system.loadlibrary();的参数“hello”是动态库的名字。
main()方法是函数得入口点。
2) 编译
javac helloworld.java
3) 生成扩展名为h的头文件
javahhelloworld
头文件的内容:
/* do not edit this file - it is machine generated */
#include
/* header for class helloworld */
#ifndef _included_helloworld
#define _included_helloworld
#ifdef __cplusplus
extern "c" {
#endif
/*
* class: helloworld
* method: displayhelloworld
* signature: ()v
*/
jniexport void jnicall java_helloworld_displayhelloworld(jnienv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
(这里我们可以这样理解:这个h文件相当于我们在java里面的接口,这里声明了一个java_helloworld_displayhelloworld (jnienv *, jobject);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写c/c++程序的时候所使用的方法名必须和这里的一致)。
4) 编写本地方法(c/c++程序)
实现和由javah命令生成的头文件里面声明的方法名相同的方法。
代码2:
1 #include "jni.h"
2 #include "helloworld.h"
3 #include ......
4jniexport void jnicall java_helloworld_displayhelloworld(jnienv *env, jobject obj)
{
printf("hello world! ");
return;
}
注意第1行,需要将jni.h(该文件可以在%java_home%/include文件夹下面找到)文件引入,因为在程序中的jnienv、jobject等类型都是在该头文件中定义的;
另外在第2行需要将helloworld.h头文件引入(我是这么理解的:相当于我们在编写java程序的时候,实现一个接口的话需要声明才可以,这里就是将helloworld.h头文件里面声明的方法加以实现。当然不一定是这样)。然后保存为helloworldimpl.c就ok了。
5) 生成动态库
这里以在windows中为例,需要生成dll文件。在保存helloworldimpl.c文件夹下面,使用vc的编译器cl生成。
命令行如下:cl -i%java_home%\include -i%java_home%\include\win32 -ld helloworldimp.c -fehello.dll
注意:生成的dll文件名在选项-fe后面配置,这里是hello,因为在helloworld.java文件中我们loadlibary的时候使用的名字是hello。当然这里修改之后那里也需要修改。另外需要将-i%java_home%\include -i%java_home%\include\win32参数加上,因为在第四步里面编写本地方法的时候引入了jni.h文件。
6) 运行程序
java helloworld就ok。
三、jni(java native interface)的简要使用例子
下面是一个简单的例子实现打印一句话的功能,但是用的c的printf最终实现。一般提供给java的jni接口包括一个so文件(封装了c函数的实现)和一个java文件(需要调用path的类)。
1. jni的目的是使java方法中能够调用c实现的一些函数,比如以下的java类,就需要调用一个本地函数testjni(一般声明为private native类型),首先需要创建文件weiqiong.java,内容如下:
class weiqiong
{
static
{
system.loadlibrary("testjni");//载入静态库,test函数在其中实现
}
private native void testjni(); //声明本地调用
public void test()
{
testjni();
}
public static void main(string args[])
{
weiqiong haha = new weiqiong();
haha.test();
}
}
2.然后执行javac weiqiong.java,如果没有报错,会生成一个weiqiong.class。
3.然后执行javah weiqiong,会生成一个文件weiqiong.h文件,其中有一个函数的声明如下:
jniexport void jnicall java_weiqiong_testjni(jnienv *, jobject);
4.创建文件testjni.c将上面那个函数实现,内容如下:
#include "jni.h"
#include weiqiong.h
jniexport void jnicall java_weiqiong_testjni(jnienv *env, jobject obj)
{
printf("haha---------go into c!!! ");
}
5.为了生成.so文件,创建makefile文件如下:
libtestjni.so:testjni.o makefile
gcc -wall -rdynamic -shared -o libtestjni.so testjni.o
testjni.o:testjni.c weiqiong.h
gcc -wall -c testjni.c -i./ -i/usr/java/j2sdk1.4.0/include -i/usr/java/j2sdk1.4.0/include/linux
cl:
rm -rf *.o *.so
注意:gcc前面是tab空,j2sdk的目录根据自己装的j2sdk的具体版本来写,生成的so文件的名字必须是loadlibrary的参数名前加“lib”。
6.export ld_library_path=.,由此设置library路径为当前目录,这样java文件才能找到so文件。一般的做法是将so文件copy到本机的ld_library_path目录下。
7.执行java weiqiong,打印出结果:“haha---------go into c!!!”
四.jni(java native interface)调用中考虑的问题
在首次使用jni的时候有些疑问,后来在使用中一一解决,下面就是这些问题的备忘:
1。 java和c是如何互通的?
其实不能互通的原因主要是数据类型的问题,jni解决了这个问题,例如那个c文件中的jstring数据类型就是java传入的string对象 ,经过jni函数的转化就能成为c的char*。
对应数据类型关系如下表:
java 类型
本地c类型
说明
boolean
jboolean
无符号,8 位
byte
jbyte
无符号,8 位
char
jchar
无符号,16 位
short
jshort
有符号,16 位
int
jint
有符号,32 位
long
jlong
有符号,64 位
float
jfloat
32 位
double
jdouble
64 位
void
void
n/a
jni 还包含了很多对应于不同 java 对象的引用类型
2. 如何将java传入的string参数转换为c的char*,然后使用?
java传入的string参数,在c文件中被jni转换为jstring的数据类型,在c文件中声明char* test,然后test = (char*)(*env)->getstringutfchars(env, jstring, null);注意:test使用完后,通知虚拟机平台相关代码无需再访问:(*env)->releasestringutfchars(env, jstring, test);
3. 将c中获取的一个char*的buffer传递给java?
这个char*如果是一般的字符串的话,作为string传回去就可以了。如果是含有’\0’的buffer,最好作为bytearray传出,因为可以制定copy的length,如果copy到string,可能到’\0’就截断了。
有两种方式传递得到的数据:
一种是在jni中直接new一个byte数组,然后调用函数(*env)->setbytearrayregion(env, bytearray, 0, len, buffer);将buffer的值copy到bytearray中,函数直接return bytearray就可以了。
一种是return错误号,数据作为参数传出,但是java的基本数据类型是传值,对象是传递的引用,所以将这个需要传出的byte数组用某个类包一下,如下:
class retobj
{
public byte[] bytearray;
}
这个对象作为函数的参数retobj传出,通过如下函数将retobj中的byte数组赋值便于传出。代码如下:
jclass cls;
jfieldid fid;
jbytearray bytearray;
bytearray = (*env)->newbytearray(env,len);
(*env)->setbytearrayregion(env, bytearray, 0, len, buffer);
cls = (*env)->getobjectclass(env, retobj);
fid = (*env)->getfieldid(env, cls, "retbytes", "[b"]);
(*env)->setobjectfield(env, retobj, fid, bytearray);
4. 不知道占用多少空间的buffer,如何传递出去呢?
在jni的c文件中new出空间,传递出去。java的数据不初始化,指向传递出去的空间即可。
五.jni中对java传入数据的处理
如果传入的是bytearray的话,作如下处理得到buffer:
char *tmpdata = (char*)(*env)->getbytearrayelements(env, bytearray, null);
(*env)->releasebytearrayelements(env, bytearray, tmpdata, 0);
六.java调用本地接口后的数据处理问题
1. java得到的数据是string的话,直接处理就可以了。
2. 得到的如果是bytearray的话,作如下处理:
datainputstream in = new datainputstream(new bytearrayinputstream(bytearray));
byte []byte1 = new byte[36];
in.read(byte1,0,36);
string string = new string(byte1);
system.out.println("读出的第一个字段为:"+string);
转载地址:http://blog.csdn.net/drean725/archive/2007/05/07/1599116.aspx
作者:crazypebble
出处:http://crazypebble.cnblogs.com/
欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
绿色通道:好文要顶关注我收藏该文与我联系
======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
- 转】JNI(Java Native Interface)的简介
- 【转】JNI(Java Native Interface)的简介
- Java Native Interface(JNI)简介
- Java Native Interface (JNI)简介
- JNI(Java Native Interface)的使用
- JNI(Java Native Interface)的使用
- JNI(Java Native Interface)的使用
- java native interface JNI 简介、基本原理
- java native interface JNI 简介、基本原理
- JNI(Java Native Interface)
- JNI(Java Native Interface)
- JNI(Java Native Interface)的简介(从C到java)
- JNI:Java Native Interface
- JNI(java native interface)
- JNI - Java Native Interface
- JNI:Java Native Interface
- Java Native Interface (JNI)
- Java Native Interface (JNI)
- Java克隆(Clone)的应用
- 从java apk中调用 Flex air apk 的方法
- java基础之基本类型与包装类
- Java学习笔记——伪代码及程序控制
- java中的数学计算函数
- 【转】JNI(Java Native Interface)的简介
- NSIS脚本制作Java程序的EXE启动器
- 在C++中嵌入JavaScript——Google V8 JavaScript Engine使用体验
- Java循环:想说爱你不容易
- java心得
- javascript 对象
- Java语法总结 - 线程
- Java garbage collection
- C# ASP.NET程序员整合Java门户单点登录PHPwind论坛博客软件集成项目经验总结