`
conkeyn
  • 浏览: 1504261 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

使用JNI进行混合编程:在Java中调用C/C++本地库

    博客分类:
  • Java
 
阅读更多

转自:http://hi.baidu.com/injava/item/b9bf4de201e32210595dd878

 

JNI是Java Native Interface的英文缩写, 中文翻译为本地调用, 自从Java 1.1开始就成为了Java标准的一部分。

AD:

JNI是Java Native Interface的英文缩写, 中文翻译为本地调用, 自从Java 1.1开始就成为了Java标准的一部分。

C/C++是系统级的编程语言, 可以用来开发任何和系统相关的程序和类库, 但是Java本身编写底层的应用比较难实现, 使用JNI可以调用现有的本地库, 极大地灵活了Java的开发。

C/C++的效率是目前最好的语言, 可以使用C/C++来实现一些实时性非常高的部分. C/C++和Java本身都是非常流行的编程语言, 一些大型软件中经常使用语言之间的混合编程。

鉴于目前网络上JNI的文章不是特别多, 我将自己的一些总结写在这里. 如有错漏, 欢迎指正!

Java调用C/C++大概有这样几个步骤

  • 编写带有native方法的Java类, 使用javac工具编译Java类
  • 使用javah来生成与native方法对应的头文件
  • 实现相应的头文件, 并编译为动态链接库(windows下是.dll, linux下是.so)

下 面就完整的介绍一个简单的Java调用C/C++的例子, 这个例子是来自http://www.ibm.com/developerworks/cn/education/java/j-jni /index.html, 不过其中有一些错误, 这个文章是非常不错的JNI学习资料, 但是非常古老.

编写Java类

我们来编写一个Sample1的java类

  1. 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[].

其中有一句比较重要, 这句话加载了动态类库

  1. 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 <jni.h>  

/* 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备注 booleanZ byteB charC shortS intI longL floatF doubleD voidV objectL用/分割的 完整类名例如: Ljava/lang/String表示String类型Array[签名例如: [I表示int数组, [Ljava/lang/String表示String数组Method(参数签名)返回类型签名例如: ([I)I表示参数类型为int数组, 返回int类型的方法

上面头文件的第一个函数声明

  1. JNIEXPORT jint JNICALL Java_Sample1_intMethod (JNIEnv *, jobject, jint); 

注释中的签名是 Signature: (I)I

在每个函数的参数列表中都有JNIEnv *和 jobject两个参数, 这两个参数稍候说明。

实现头文件中的函数

可以使用C语言来实现, 也可以使用C++来实现, 下面先说说C语言的实现。

#include "Sample1.h" 

#include <string.h>  

 

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 <string.h>  

 

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

分享到:
评论

相关推荐

    java与C++的相互调用

    java与C++的相互调用,使用JNI进行Java与C/C++语言混合编程。

    Java与VC++混合编程实例演示程序

    其它分类,混合编程,Java Java与VC++混合编程的实例演示,实现的原理:实现Java 和C++的交互,使用的技术是称为JNI( Java Native Interface ), C++编写的程序,只要实现JNI 生成的接口,则可以让Java 程序调用,...

    h_JAVA 2应用编程150例.rar

    第6章 Java C/S结构编程 197 实例67 实现C/S多线程 198 实例68 客户端程序 200 实例69 服务器端程序 201 实例70 C/S结构聊天室 203 实例71 基于C/S的小游戏 209 实例72 应用C/S数据库 237 实例73 实现客户端程序 242...

    java开源包1

    开发它是用于在UTF-8 Oracle实例中使用ASCII编码的Oracle 数据库中来正确的传输非ASCII字符。 Java模板语言 Beetl Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,...

    JAVA上百实例源码以及开源项目源代码

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    JAVA上百实例源码以及开源项目

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    java开源包8

    开发它是用于在UTF-8 Oracle实例中使用ASCII编码的Oracle 数据库中来正确的传输非ASCII字符。 Java模板语言 Beetl Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,...

    java开源包4

    开发它是用于在UTF-8 Oracle实例中使用ASCII编码的Oracle 数据库中来正确的传输非ASCII字符。 Java模板语言 Beetl Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,...

    java开源包11

    开发它是用于在UTF-8 Oracle实例中使用ASCII编码的Oracle 数据库中来正确的传输非ASCII字符。 Java模板语言 Beetl Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,...

    java开源包6

    开发它是用于在UTF-8 Oracle实例中使用ASCII编码的Oracle 数据库中来正确的传输非ASCII字符。 Java模板语言 Beetl Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,...

    java开源包9

    开发它是用于在UTF-8 Oracle实例中使用ASCII编码的Oracle 数据库中来正确的传输非ASCII字符。 Java模板语言 Beetl Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,...

    java开源包101

    开发它是用于在UTF-8 Oracle实例中使用ASCII编码的Oracle 数据库中来正确的传输非ASCII字符。 Java模板语言 Beetl Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,...

    java开源包5

    开发它是用于在UTF-8 Oracle实例中使用ASCII编码的Oracle 数据库中来正确的传输非ASCII字符。 Java模板语言 Beetl Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,...

    java开源包10

    开发它是用于在UTF-8 Oracle实例中使用ASCII编码的Oracle 数据库中来正确的传输非ASCII字符。 Java模板语言 Beetl Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,...

    java开源包3

    开发它是用于在UTF-8 Oracle实例中使用ASCII编码的Oracle 数据库中来正确的传输非ASCII字符。 Java模板语言 Beetl Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,...

    Java资源包01

    开发它是用于在UTF-8 Oracle实例中使用ASCII编码的Oracle 数据库中来正确的传输非ASCII字符。 Java模板语言 Beetl Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,...

    java开源包2

    开发它是用于在UTF-8 Oracle实例中使用ASCII编码的Oracle 数据库中来正确的传输非ASCII字符。 Java模板语言 Beetl Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,...

    java开源包7

    开发它是用于在UTF-8 Oracle实例中使用ASCII编码的Oracle 数据库中来正确的传输非ASCII字符。 Java模板语言 Beetl Beetl,是Bee Template Language的缩写,它绝不是简单的另外一种模板引擎,而是新一代的模板引擎,...

Global site tag (gtag.js) - Google Analytics