Java调用C/C++生成的动态库函数

问题背景

之前的文章中,笔者将超长整数的四则运算利用C语言实现,因个人需要在web项目中使用该功能,

此时能想到的办法是重写实现过程,即利用Java重写一遍C的实现过程

不谈工作量的多少,单单是这个重写的过程就让我望而生畏,程序员最头疼的一个是bug找不到,还有一个就是重复劳动。

解决思路:

一个很偶然的机会让我看到了不需要重复劳动的希望--JNI

由于之前从来没有跨语种合作一个工程的经验,因此本篇将记录个人的实践过程

解决过程:

1、导入JNI的头文件jni.hjni_md.h

用“导入”这个词其实就是一个包含关系,在实际编码过程中用户可以选择将两个头文件复制到本地项目工程目录或者根本就不用做任何移动操作

该文件位于JDK安装路径的include目录下(其实不止一处,开发人员可以根据自己的系统winows/Linux搜索)。

2、Java侧编写native方法

package org.demo.jni;

   public abstract class ICalcHBInteger {
       public native int init_HBInt();
       public native void input_HBInt(String in);
       public native String output_HBInt();
       public native String add_HBInt(String a1,String a2);
       public native String sub_HBInt(String s1,String s2);
       public native String mul_HBInt(String m1,String m2);
       public native String div_HBInt(String d1,String d2);
       public native String mod_HBInt(String mod1,String mod2);
       public native String powerMod_HBInt(String p1,String p2, String p3);
  }

这些方法的名称自定义,对参数个数、类型没有任何限制,但是一定要加上native关键字

(注:示例中用的是抽象类,这只是笔者个人习惯,不是必须的,同时在类中也可以定义其它非native 的方法,用户自行斟酌)

3、利用javah命令生成.h头文件

利用定义好的含有native方法类进行转换,改转换由javah命令完成

即:javah -jni org.demo.jni.ICalcHBInteger

正确执行后,会在当前执行命令的目录下生成org_demo_jni_ICalcHBInteger.h文件

文件内容类似如下:

/*
   * Class:     org_demo_jni_ICalcHBInteger
   * Method:    output_HBInt
   * Signature: ()Ljava/lang/String;
   */
  JNIEXPORT jstring JNICALL Java_org_demo_jni_ICalcHBInteger_output_1HBInt
    (JNIEnv *, jobject);

4、在自定义的C文件中实现该头文件定义的方法

生成了头文件后,就是对头文件声明的方法进行实现,此时因为在C侧已经完成了主要的方法(笔者采用的是C实现超长整数),

所以只需要在自定义的C文件中利用已经实现的方法来填充

Java_org_demo_jni_ICalcHBInteger_output_1HBInt

例如,笔者自定义的是HBIntegerVM.c文件,而已经实现的超长整数文件名为HBInteger.c(其对应的头文件是HBInteger.h)

HBIntegerVM.c内容如下:

JNIEXPORT jstring JNICALL Java_com_zsf_jni_ICalcHBInteger_output_1HBInt(
          JNIEnv *env, jobject obj) {
      int ret;
      ret = writeHBInt(&last, result);
      if (ret) {
          printf(
                  "Error in exec func [writeHBInt(&last, result.\n");
      }
      return nativeTojstring(env, result);
 }

需要说明的是 writeHBInt 方法就是 已经实现在HBInteger.c文件中的。类比于用户,就是已经完成的C语言方法。

5、生成动态库,以备Java侧可以调用

因笔者使用的是Linux系统,因此编译生成的是.so文件,如果是windows,则需要生成dll文件。

生成动态库文件编译命令:gcc -I/usr/lib/jvm/java-7-openjdk-i386/include -fPIC -shared -o libHBIntegerVM.so HBIntegerVM.c HBInteger.c

需要特别说明的是 -I(是i的大写) 参数必须有,因为使用了jni.h头文件,至于路径则依赖于各个系统安装的JDK版本和路径,笔者的路径是默认JDK安装路径

如果是集成环境则需要把 -I 的路径值加入到编译path里,因为集成工具可能不同,这里无法具体操作,请用户自行查找解决办法。

如果一切顺利,则生成的是 libHBIntegerVM.so 文件,一定要 使用lib开头,并且以 .so 结尾(Linux要求),windows没有这个限制,只要.dll结尾即可。

6、将动态库加入Java项目的JNI路径中

因笔者使用的是eclipse,所以直接在工程的 Build path 处 添加了Native JNI 路径

(即动态库所在目录的路径,可以自定义一个路径,将生成的库文件拷贝到指定路径,也可以直接定位到生成的库文件所在路径)

7、使用库文件中的方法

例如:

 public class TestJNI extends ICalcHBInteger{

       static{
           System.loadLibrary("HBIntegerVM");
       }
       /**
        * @param args
       */
      public static void main(String[] args) {
          // TODO Auto-generated method stub
          TestJNI test = new TestJNI();
          test.init_HBInt();

          String get;

          test.add_HBInt("12345678909876543210", "9876543210123456789");
          get = test.output_HBInt();
          System.out.println("return: " + get);
     }
}

需要说明的是:

System.loadLibrary("HBIntegerVM");

就是在加载动态库,其中 HBIntegerVM 就是去掉库文件的头和尾 即:lib 和 .so  如果是windows平台的dll文件,则只去掉尾,即 .dll 部分

还看到

test.output_HBInt();

就是之前代码中的native方法名

小结:

1、JNI技术目前应用不是很广泛,究其原因是因为这样做影响了调用层的性能(主要是交互时间长)

2、在非必要情况下(比如笔者这种情况就属于个人为了偷懒),尽量不要使用该技术,以免带来性能和调试上的麻烦。

3、在使用过程中,如果涉及到传递参数,一定要注意参数类型的转换,具体转换方法,根据类型的不同,方法各异。

4、在进行工程分块时,特别是针对积木类型结构的工程,该方式可以值得借鉴并加以利用,甚至优化。

时间: 2024-10-30 09:05:25

Java调用C/C++生成的动态库函数的相关文章

Java反射—运用反射生成jdk动态代理

1.  核心类&接口 在Java的java.lang.reflect包下提供一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口可以生成jdk动态代理类或动态代理对象. Proxy是所有动态代理类的父类,它提供了两个静态方法来创建动态代理类和动态代理对象,如下: ?  static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) ?  static Objec

JNI java 调c++动态库函数,多值回传及乱码问题

一,JNI java 调c++动态库函数的问题 1.多参数回传 2.参数传递出现乱码 二,解决 1.使用byte[]数组传入c++,在生成的头文件里就会变成jbyteArray 类型 例如,java 中参数:byte[]account,头文件里参数变成jbyteArray account, 通过c++修改完account的值后,java要获取该值,直接使用 jbyte* jbAccount = (env)->GetByteArrayElements(env, account, 0); char*

java调用dll或so动态库文件(c++/c)

java调用dll或so动态库文件(c++/c) 博客分类: 工作 CC#C++JavaEclipse java调用dll或so动态库文件(c++/c)开发平台:Eclipse3.3.1.1+CDT(cdt-master-4.0.3)+MinGW(MinGW-5.1.4) 一:下面是java调用dll(C++) 1:下载并安装cdt :http://www.eclipse.org/cdt/downloads.php  :选择自己eclipse 支持的cdt插件,下载,并且 通过eclipse--

java 调用 keytool 生成keystore 和 cer 证书

keytool是一个Java数据证书的管理工具, keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中在keystore里, 包含两种数据: 密钥实体(Key entity)——密钥(secret key)又或者是私钥和配对公钥(采用非对称加密) 可信任的证书实体(trusted certificate entries)——只包含公钥 ailas(别名)每个keystore都关联这一个独一无二的alias,这个alias通常不区分大小写 下面给出一

java调用phantomjs采集ajax加载生成的网页

java调用phantomjs采集ajax加载生成的网页 日前有采集需求,当我把所有的对应页面的链接都拿到手,准备开始根据链接去采集(写爬虫爬取)对应的终端页的时候,发觉用程序获取到的数据根本没有对应的内容,可是我的浏览器看到的内容明明是有的,于是浏览器查看源代码也发觉没有,此时想起该网页应该是ajax加载的.不知道ajax的小朋友可以去学下web开发啦. 采集ajax生成的内容手段不外乎两种.一种是通过http观察加载页面时候的请求,然后我们模仿该请求去得到对应的内容,第二种则是模仿浏览器行为

【jni编程】—— 从Java调用C/C++

                                                  从Java调用C/C++ 当无法用 Java 语言编写整个应用程序时,JNI 允许您调用C/C++本机代码.在下列典型情况下,您可能决定使用本机代码: 希望用更低级.更快的编程语言C/C++去实现对时间有严格要求的代码. 希望从 Java 程序访问旧代码或代码库. 需要标准 Java 类库中不支持的依赖于平台的特性. 我需要它的代码背景 我在安卓项目中,需要用到C++的soundtouch库函数.

java调用c++ dll出现中文乱码

最近的开发用到了使用java调用本机动态连接库的功能,将文件路径通过java调用C++代码对文件进行操作.在调用中如果路径中包含有中文字符就会出现问题,程序运行就会中止.下面用一个小例子,来说明记录下解决的方法. java中传入一个字符串,调用c++代码将字符串输出 public class CommonUtil { static { System.loadLibrary("nativeTest"); } public native static void Print(String s

android开发教程(4)— jni编程之采用 javah 从java调用C++

用Java调用C/C++代码 当无法用 Java 语言编写整个应用程序时,JNI 允许您使用本机代码.在下列典型情况下,您可能决定使用本机代码: 希望用更低级.更快的编程语言去实现对时间有严格要求的代码. 希望从 Java 程序访问旧代码或代码库. 需要标准 Java 类库中不支持的依赖于平台的特性. 须知:SWIG和javah的区别(强烈推荐) 我看了网上的关于 jni编程 的教程很多,但不尽相同,刚开始会犯迷糊.我想笔者往往忽略了一个关键点,那就是采用了什么方式决定了步骤的流程.有两种生成

嵌入式 Linux C语言(十)——静态库函数和动态库函数

嵌入式 Linux C语言(十一)--静态库函数和动态库函数 一.静态链接库 静态链接库是obj文件的一个集合,通常静态链接库以".a"为后缀,名字格式一般为libxxx.a,由程序ar生成.静态链接库是在程序编译过程中链接的,已经将调用的相关函数拷贝到程序内部,程序运行时和静态链接库已经没有任何关系. 1.静态链接库的创建 A.编写源码库文件 源码库文件一般包含.c和.h文件, hello.c文件: #include <stdio.h> void display(void