JNI是Java Native Interface的英文缩写, 中文翻译为本地调用, 自从Java 1.1开始就成为了Java标准的一部分。
公司主营业务:成都网站建设、成都网站制作、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。创新互联是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。创新互联推出西华免费做网站回馈大家。
C/C++是系统级的编程语言, 可以用来开发任何和系统相关的程序和类库, 但是Java本身编写底层的应用比较难实现, 使用JNI可以调用现有的本地库, 极大地灵活了Java的开发。
C/C++的效率是目前***的语言, 可以使用C/C++来实现一些实时性非常高的部分. C/C++和Java本身都是非常流行的编程语言, 一些大型软件中经常使用语言之间的混合编程。
鉴于目前网络上JNI的文章不是特别多, 我将自己的一些总结写在这里. 如有错漏, 欢迎指正!
Java调用C/C++大概有这样几个步骤
下面就完整的介绍一个简单的Java调用C/C++的例子, 这个例子是来自http://www.ibm.com/developerworks/cn/education/java/j-jni/index.html, 不过其中有一些错误, 这个文章是非常不错的JNI学习资料, 但是非常古老.
编写Java类
我们来编写一个Sample1的java类
- public class Sample1 {
- public native int intMethod(int n);
- public native boolean booleanMethod(boolean bool);
- public native String stringMethod(String text);
- public native int intArrayMethod(int[] intArray);
- public static void main(String[] args) {
- System.loadLibrary("Sample1");
- Sample1 sample = new Sample1();
- int square = sample.intMethod(5);
- boolean bool = sample.booleanMethod(true);
- String text = sample.stringMethod("Java");
- int sum = sample.intArrayMethod(new int[]{1,2,3,4,5,8,13});
- System.out.println("intMethod: " + square);
- System.out.println("booleanMethod: " + bool);
- System.out.println("stringMethod: " + text);
- System.out.println("intArrayMethod: " + sum);
- }
- }
上面有4个native方法, 分别是4种类型的参数, int, boolean, String, int[].
其中有一句比较重要, 这句话加载了动态类库
- System.loadLibrary("Sample1");
在windows下加载的就是Sample1.dll, 在linux下加载的就是Sample1.so。
本文使用的windowws, 所以后面使用Sample1.dll来表示Sample1动态链接库。
注意:不可以在代码中写上后缀dll或so. 还要保证Sample1.dll在path路径中. 这个Sample1.dll是我们后面需要编译出来的东西。
4个native方法就是我们需要用C来实现的方法。
编译Sample1.java, 使用命令行(windows是cmd, linux下一般是bash)
>javac Sample1.java
可以看到Sample1.class文件
使用javah生成头文件
在命令行中运行
>javah Sample1
可以在目录下看到一个新文件Sample1.h
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include
- /* Header for class Sample1 */
- #ifndef _Included_Sample1
- #define _Included_Sample1
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: Sample1
- * Method: intMethod
- * Signature: (I)I
- */
- JNIEXPORT jint JNICALL Java_Sample1_intMethod
- (JNIEnv *, jobject, jint);
- /*
- * Class: Sample1
- * Method: booleanMethod
- * Signature: (Z)Z
- */
- JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod
- (JNIEnv *, jobject, jboolean);
- /*
- * Class: Sample1
- * Method: stringMethod
- * Signature: (Ljava/lang/String;)Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_Sample1_stringMethod
- (JNIEnv *, jobject, jstring);
- /*
- * Class: Sample1
- * Method: intArrayMethod
- * Signature: ([I)I
- */
- JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod
- (JNIEnv *, jobject, jintArray);
- #ifdef __cplusplus
- }
- #endif
- #endif
我们可以看到其中有四个函数声明, Java_完整类名_方法名, 完整类名包括了包名, 例如demo.Sample1是完整类名, 对应的这里就是demo_Sample1.
在注释中我们可以看到这样一个东西 Signature, 这个是方法的签名. 关于Signature, 下面通过一个表格来说明。
java类型 | Signature | 备注 |
boolean | Z | |
byte | B | |
char | C | |
short | S | |
int | I | |
long | L | |
float | F | |
double | D | |
void | V | |
object | L用/分割的完整类名 | 例如: Ljava/lang/String表示String类型 |
Array | [签名 | 例如: [I表示int数组, [Ljava/lang/String表示String数组 |
Method | (参数签名)返回类型签名 | 例如: ([I)I表示参数类型为int数组, 返回int类型的方法 |
上面头文件的***个函数声明
- JNIEXPORT jint JNICALL Java_Sample1_intMethod (JNIEnv *, jobject, jint);
注释中的签名是 Signature: (I)I
在每个函数的参数列表中都有JNIEnv *和 jobject两个参数, 这两个参数稍候说明。
实现头文件中的函数
可以使用C语言来实现, 也可以使用C++来实现, 下面先说说C语言的实现。
- #include "Sample1.h"
- #include
- JNIEXPORT jint JNICALL Java_Sample1_intMethod
- (JNIEnv *env, jobject obj, jint num)
- {
- return num * num;
- }
- JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod
- (JNIEnv *env, jobject obj, jboolean boolean)
- {
- return !boolean;
- }
- JNIEXPORT jstring JNICALL Java_Sample1_stringMethod
- (JNIEnv *env, jobject obj, jstring string)
- {
- const char* str = (*env)->GetStringUTFChars(env, string, 0);
- char cap[128];
- strcpy(cap, str);
- (*env)->ReleaseStringUTFChars(env, string, 0);
- return (*env)->NewStringUTF(env, strupr(cap));
- }
- JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod
- (JNIEnv *env, jobject obj, jintArray array)
- {
- int i, sum = 0;
- jsize len = (*env)->GetArrayLength(env, array);
- jint *body = (*env)->GetIntArrayElements(env, array, 0);
- for (i = 0; i < len; ++i)
- {
- sum += body[i];
- }
- (*env)->ReleaseIntArrayElements(env, array, body, 0);
- return sum;
- }
(*env)->GetStringUTFChars()这个方法, 是用来在Java和C之间转换字符串的, 因为Java本身都使用了双字节的字符, 而C语言本身都是单字节的字符, 所以需要进行转换.
JNIEnv *是每个函数都有的参数, 它包含了很多有用的方法, 使用起来类似Java的反射, 也提供了这样一个编码转换的函数.
GetStringUTFChars()和NewStringUTF(), ***个是从UTF8转换为C的编码格式, 第二个是根据C的字符串返回一个UTF8字符串.
ReleaseStringUTFChars()是用来释放对象的, 在Java中有虚拟机进行垃圾回收, 但是在C语言中, 这些对象必须手动回收. 否则可能造成内存泄漏.
函数的名字一眼看到就可以猜出功能, jni.h中的大部分函数名都是这样.
如果是C++的话, 这段代码该怎么写?
下面是C++的代码
- #include "Sample1.h"
- #include
- JNIEXPORT jint JNICALL Java_Sample1_intMethod
- (JNIEnv *env, jobject obj, jint num)
- {
- return num * num;
- }
- JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod
- (JNIEnv *env, jobject obj, jboolean boolean)
- {
- return !boolean;
- }
- JNIEXPORT jstring JNICALL Java_Sample1_stringMethod
- (JNIEnv *env, jobject obj, jstring string)
- {
- const char* str = env->GetStringUTFChars(string, 0);
- char cap[128];
- strcpy(cap, str);
- env->ReleaseStringUTFChars(string, 0);
- return env->NewStringUTF(strupr(cap));
- }
- JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod
- (JNIEnv *env, jobject obj, jintArray array)
- {
- int i, sum = 0;
- jsize len = env->GetArrayLength(array);
- jint *body = env->GetIntArrayElements(array, 0);
- for (i = 0; i < len; ++i)
- {
- sum += body[i];
- }
- env->ReleaseIntArrayElements(array, body, 0);
- return sum;
- }
上述两端代码非常相似, 只有一个不同点
C代码: (*env)->GetStringUTFChars(env, string, 0);
C++代码: env->GetStringUTFChars(string, 0);
C语言中使用的是结构体的函数指针, 而在C++中使用的还是struct, 我们知道struct在C++中和class的功能是几乎一样的, struct也可以用来定义类, 所以env在C++中是个类对象的指针.
编译和运行
这里使用的是微软编译器, 编译C语言版的dll
>cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD Sample1.c -FeSample1.dll
编译C++版本的dll
>cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD Sample1.cpp -FeSample1.dll
运行
>java Sample1
注意: 64位版本的JDK可能会在运行时报错:
java.lang.UnsatisfiedLinkError: ...Sample1.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform
如果您有这样的错误, 请使用32位的JDK来重新运行.
运行结果如下:
intMethod: 25 booleanMethod: false stringMethod: JAVA intArrayMethod: 36
源代码下载: Sample1.zip
运行其中的build&run.bat文件即可, 如有错误请根据实际情况修改其中的一些参数.
DLL工程文件VC6.0和VS2010的: VC6.0&VS2010.zip
参考文献:
文章名称:使用JNI进行混合编程:在Java中调用C/C++本地库
本文来源:http://www.csdahua.cn/qtweb/news40/2790.html
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网