JNI实例浅谈
2009-06-18 11:34
92 查看
JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能。解决JAVA对本地操作的一种方法就是JNI。
JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式,在LNUIX机器上是ELF文件形式)。通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法。因为Java的编译运行都是在JVM,而它在操作系统上实际就是一个进程,因此我们只需要把C或C++的数据通过DLL,SO,ELF加载到该进程中,JVM就可实现对该数据的调用。
JNI(Java Native Interface)的书写步骤
·编写带有native声明的方法的java类
·使用javac命令编译所编写的java类
·使用javah java类名生成扩展名为h的头文件
·使用C/C++实现本地方法
·将C/C++编写的文件生成动态连接库
·把.Dll的文件放到ClassPath路径下,Java就可以实现对其数据的调用
简单介绍在WINDOS的操作如下:
一、JAVA中所需要做的工作
在JAVA程序中,首先需要在类中声明所调用的库名称,如下:
static {
System.loadLibrary(“TextDll”);
}
在这里,库的扩展名字可以不用写出来,由系统自己判断后会把格式自动加上去。
还需要对将要调用的方法做本地声明,关键字为native。并且只需要声明,而不需要具 体实现。如下:
public native static void set(String name);
public native static String get();
然后编译该JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就会生成C/C++的头文件。
例如程序TextDll.java,内容为:
public class TextDll
{
static
{
System.loadLibrary(“TextDll”);
}
public native static String get();
public native static void set(String name);
public static void main(String[] args)
{
TextDll test = new TextDll l();
test.set(“bluesky”);
System.out.println(test.get());
}
}
用javac TextDll .java编译它,会生成TextDll .class。
再用javah TextDll,则会在当前目录下生成TextDll.h文件,这个文件需要被C/C++程序调用来生成所需的库文件。
二、C/C++中所需要做的工作
对于已生成的.h头文件,C/C++所需要做的,就是把它的各个方法具体的实现。然后编译连接成库文件即可。再把库文件拷贝到JAVA程序的路径下面,就可以用JAVA调用C/C++所实现的功能了。
接上例子。我们先看一下TextDll.h文件的内容:
#include
#ifndef _Included_TextDl
#define _Included_TextDl
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_TextDll_get (JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_ TextDll _set (JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
在具体实现的时候,我们只关心两个函数原型
JNIEXPORT jchar JNICALL Java_ TextDll _get (JNIEnv *, jclass); 和
JNIEXPORT void JNICALL Java_ TextDll _set (JNIEnv *, jclass, jstring);
这里JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。而jstring是以JNI为中介使JAVA的String类型与本地的char沟通的一种类型,我们可以视而不见,就当做String使用。函数的名称是JAVA_再加上java程序的package路径再加函数名组成的。参数中,我们也只需要关心在JAVA程序中存在的参数,至于JNIEnv*和jclass我们一般没有必要去碰它。
好,下面我们用TextDll.cpp文件具体实现这两个函数:
#include "TextDll.h"
char name[]={”bluesky”};
char* test;
JNIEXPORT jchar JNICALL Java_TextDll_get (JNIEnv *, jclass)
{
return “Text Success!”;
(*env)->ReleaseStringUTFChars(env, jstring, test);
}
JNIEXPORT void JNICALL Java_ TextDll _set (JNIEnv *, jclass, jchar tName)
{
test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);
}
编译连接成库文件,本例是在WINDOWS下做的,生成的是DLL文件。并且名称要与JAVA中需要调用的一致,这里就是TextDll.dll 。把TextDll .dll拷贝到TextDll.class的目录下,javaTextDll运行它,就可以观察到结果了。
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
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)->NewByteArr
4000
ay(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的数据不初始化,指向传递出去的空间即可。
JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式,在LNUIX机器上是ELF文件形式)。通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法。因为Java的编译运行都是在JVM,而它在操作系统上实际就是一个进程,因此我们只需要把C或C++的数据通过DLL,SO,ELF加载到该进程中,JVM就可实现对该数据的调用。
JNI(Java Native Interface)的书写步骤
·编写带有native声明的方法的java类
·使用javac命令编译所编写的java类
·使用javah java类名生成扩展名为h的头文件
·使用C/C++实现本地方法
·将C/C++编写的文件生成动态连接库
·把.Dll的文件放到ClassPath路径下,Java就可以实现对其数据的调用
简单介绍在WINDOS的操作如下:
一、JAVA中所需要做的工作
在JAVA程序中,首先需要在类中声明所调用的库名称,如下:
static {
System.loadLibrary(“TextDll”);
}
在这里,库的扩展名字可以不用写出来,由系统自己判断后会把格式自动加上去。
还需要对将要调用的方法做本地声明,关键字为native。并且只需要声明,而不需要具 体实现。如下:
public native static void set(String name);
public native static String get();
然后编译该JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就会生成C/C++的头文件。
例如程序TextDll.java,内容为:
public class TextDll
{
static
{
System.loadLibrary(“TextDll”);
}
public native static String get();
public native static void set(String name);
public static void main(String[] args)
{
TextDll test = new TextDll l();
test.set(“bluesky”);
System.out.println(test.get());
}
}
用javac TextDll .java编译它,会生成TextDll .class。
再用javah TextDll,则会在当前目录下生成TextDll.h文件,这个文件需要被C/C++程序调用来生成所需的库文件。
二、C/C++中所需要做的工作
对于已生成的.h头文件,C/C++所需要做的,就是把它的各个方法具体的实现。然后编译连接成库文件即可。再把库文件拷贝到JAVA程序的路径下面,就可以用JAVA调用C/C++所实现的功能了。
接上例子。我们先看一下TextDll.h文件的内容:
#include
#ifndef _Included_TextDl
#define _Included_TextDl
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_TextDll_get (JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_ TextDll _set (JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
在具体实现的时候,我们只关心两个函数原型
JNIEXPORT jchar JNICALL Java_ TextDll _get (JNIEnv *, jclass); 和
JNIEXPORT void JNICALL Java_ TextDll _set (JNIEnv *, jclass, jstring);
这里JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。而jstring是以JNI为中介使JAVA的String类型与本地的char沟通的一种类型,我们可以视而不见,就当做String使用。函数的名称是JAVA_再加上java程序的package路径再加函数名组成的。参数中,我们也只需要关心在JAVA程序中存在的参数,至于JNIEnv*和jclass我们一般没有必要去碰它。
好,下面我们用TextDll.cpp文件具体实现这两个函数:
#include "TextDll.h"
char name[]={”bluesky”};
char* test;
JNIEXPORT jchar JNICALL Java_TextDll_get (JNIEnv *, jclass)
{
return “Text Success!”;
(*env)->ReleaseStringUTFChars(env, jstring, test);
}
JNIEXPORT void JNICALL Java_ TextDll _set (JNIEnv *, jclass, jchar tName)
{
test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);
}
编译连接成库文件,本例是在WINDOWS下做的,生成的是DLL文件。并且名称要与JAVA中需要调用的一致,这里就是TextDll.dll 。把TextDll .dll拷贝到TextDll.class的目录下,javaTextDll运行它,就可以观察到结果了。
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
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)->NewByteArr
4000
ay(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的数据不初始化,指向传递出去的空间即可。
相关文章推荐
- Android系统中硬件访问服务框架(JNI HAL)及实例
- Android——JNI调用简单实例解析
- ADT + JNI实例
- (转)浅谈数据库设计技巧(实例一定的数据冗余)
- 浅谈 Linux 下 file 的六种应用实例
- android JNI 实例代码
- (JNI)C/C++ 访问 Java 实例变量和静态变量
- Android JNI实例
- 通过实例浅谈Spring运作机制
- Android apk安装过程及Java、JNI读取安装包内assets资源文件的两种方法(附源码实例)
- 浅谈实例Page Method到静态Page Method的移植
- JNI简介及实例
- Android通过JNI调用驱动程序(完全解析实例)
- 通过实例浅谈春运作机制
- Android JNI实例
- 浅谈MVC框架中View层的优雅设计及实例
- Linux on POWER 的 JNI 编程实例
- JNI中在被调用的C/C++函数中如何访问Java程序中的类,并编写应用实例
- 呕心沥血Android studio使用JNI实例
- JNI入门及实例