从C/C++到Java,再从Java回到C/C++,今天终于有机会了解了连接Java、C/C++的桥梁——JNI。哈哈!分享一下!
 
一、简介
JNI是Java native interface的简写,可以译作Java原生接口。Java可以通过JNI调用C/C++的库,这对于那些对性能要求比较高的Java程序或者Java无法处理的任务无疑是一个很好的方式。
 
二、目的:Java代码中调用C/C++代码
三、实现:假设我们的Java程序为J2C.java, C程序为J2C.c, Java与C之间的通信函数名为write2proc;
              那么write2proc的声明位于J2C.java,实现位于J2C.c;
四、操作

1. 编写并编译Java程序

javac J2C.java => J2C.class
2. 生成C/C++头文件

javah J2C => J2C.h (安装JDK后,$JAVA_HOME应该已加入$PATH, 否则使用绝对路径,例如/usr/bin/javah)

3. 编写对应的C/C++程序:J2C.c

4. 生成C/C++目标文件
    gcc -I/usr/lib/jvm/java-6-openjdk-amd64/include -I/usr/lib/jvm/java-6-openjdk-amd64/include/linux -fPIC -c J2C.c => J2C.o
5. 生成C/C++共享库

gcc -shared -Wl,-soname,libj2c.so.1 -o libj2c.so.1.0 J2C.o => libj2c.so.1.0

6. 重命名cp libj2c.so.1.0 libj2c.so => libj2c.so

7. 将共享库加入动态链接库的路径(此例为当前目录)
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

8. 执行Java程序,实现跨语言通信
     java J2C

五、具体过程

1. 编写并编译J2C.java

 import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean; public class J2C
{
static
{
try{
// 此处即为本地方法所在链接库名
System.loadLibrary("j2c");
} catch(UnsatisfiedLinkError e)
{
System.err.println( "Cannot load J2C library:\n " +
e.toString() );
}
} //声明的本地方法
public static native int write2proc(int pid); public static void main(String[] args){ //获取本进程(即主线程)的pid
final RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
final String info = runtime.getName();
final int index = info.indexOf("@"); if (index != -1) {
final int pid = Integer.parseInt(info.substring(0, index));
System.out.println(info);
System.out.println(pid); write2proc(pid);
} try{
Thread.sleep(8000);
} catch(InterruptedException e){
e.printStackTrace();
}
}
}

note:Java程序中System.loadLibrary参数名表示要载入的C/C++共享库,第6步生成的共享库名必须与该参数一致,

即System.loadLibrary(Name) 对应共享库名libName.so (共享库名必须以lib开头)

2. 生成C头文件J2C.h:javah J2C

 /* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class J2C */ #ifndef _Included_J2C
#define _Included_J2C
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: J2C
* Method: write2proc
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_J2C_write2proc
(JNIEnv *, jclass, jint); #ifdef __cplusplus
}
#endif
#endif

note:1. 头文件自动生成,不要修改它;

2. 函数JNIEXPORT jint JNICALL Java_J2C_write2proc(JNIEnv *, jclass, jint);

按照注释的说明是在J2C.java文件的类J2C的方法write2proc处定义,故C程序的实现函数必须与该处签名一致;

3. 编写C程序J2C.c

 #include <stdio.h>

 #include "J2C.h"

 JNIEXPORT int JNICALL Java_J2C_write2proc(JNIEnv * env, jobject arg, jint pid)
{ printf("current pid is %d\n", pid); return ; }

4. 编译C程序

因为C程序里#include "J2C.h"而J2C.h又#include <jni.h>, 而gcc里面默认环境并不知道jni.h是什么东西,故编译时需要告诉编译器jni.h的位置( jni.h在jdk 的$JAVA_HOME/include下面),所以才有了上面的编译参数;

  因为使用gcc编译得到动态库,在jni调用的时候,某些情况会有异常, 可尝试改用g++。

后续的5、6、7、8同前文所述。

六、总结

1. Java中方法的原型声明与C/C++对应的实现文件定义必须一致(可以通过自动生成的C/C++头文件来比较),尤其是类名和方法名;

2. Java中System.loadLibrary()载入的共享库名必须与后面C/C++生成的共享库名一致;

3. JNI步骤很清晰,但是很多小细节容易引起各种问题,so细心点。

附上C/C++中Java方法签名及常见的错误:

1. 在C/C++函数中实现Java中定义的方法

函数声明, Java_完整类名_方法名, 完整类名包括了包名.

在注释中我们可以看到这样一个东西 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_J2C_write2proc(JNIEnv *, jobject, jint);

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

2. JNI开发中碰到的"error: request for member 'GetStringUTFChars' in something not a structure or union"

这个错误在C、C++中需要分开处理。

C代码: (*env)->GetStringUTFChars(env, string, 0);

C++代码: env->GetStringUTFChars(string, 0);

原因:C语言中使用的是结构体的函数指针, 而在C++中使用的还是struct, 我们知道struct在C++中和class的功能是几乎一样的, struct也可以用来定义类, 所以env在C++中是个类对象的指针.

下面以IBM的一篇经典文章为例 http://www.ibm.com/developerworks/cn/java/l-linux-jni/

C++:

// 从 instring 字符串取得指向字符串 UTF 编码的指针

const char *str = env->GetStringUTFChars( instring, JNI_FALSE ); 
printf("Hello,%s\n",str); 
// 通知虚拟机本地代码不再需要通过 str 访问 Java 字符串。
env->ReleaseStringUTFChars( instring, str );

C:

// 从 instring 字符串取得指向字符串 UTF 编码的指针

const char *str = (*env)->GetStringUTFChars( env, instring, JNI_FALSE ); 
printf("Hello,%s\n",str); 
// 通知虚拟机本地代码不再需要通过 str 访问 Java 字符串。
(*env)->ReleaseStringUTFChars( env, instring, str );

3. 当使用 JNI 从 Java 程序访问本机代码时,您会遇到许多问题。您会遇到的三个最常见的错误是:

无法找到动态链接:Exception in thread "main" java.lang.UnsatisfiedLinkError

它所产生的错误消息是:java.lang.UnsatisfiedLinkError。这通常指无法找到共享库,或者无法找到共享库内特定的本机方法。

无法找到共享库文件:Cannot load library

当用 System.loadLibrary(String libname) 方法(参数是文件名)装入库文件时,请确保文件名拼写正确以及没有指定扩展名。还有,确保库文件的位置在类路径中,从而确保 JVM 可以访问该库文件。

无法找到具有指定说明的方法

确保您的 C/C++ 函数实现拥有与头文件中的函数说明相同的说明。

How to resolve java.lang.UnsatisfiedLinkError 
User should check whether-

1. System.loadLibrary is passed an incorrect parameter:

- Windows: To load Name.dll, Name is passed to the loadLibrary method.

-AIX, HP-UX, Solaris, Linux: To load libName.so or libName.a, libName is passed to the loadLibrary method
2. Native library is already loaded-

If the native library was already loaded by an application and the same application tries to load it again, this can cause this error.
3. Native Library is not present in java.library.path or LD_LIBRARY_PATH

参考:

http://www.ibm.com/developerworks/cn/java/l-linux-jni/

http://www.cnblogs.com/icejoywoo/archive/2012/02/22/2363709.html

http://www.cnblogs.com/mimi1/archive/2012/09/27.html

http://stackoverflow.com/questions/11624503/i-am-trying-to-use-the-freeimage-library-in-my-project-causes-error

JNI——Java调用C/C++函数的更多相关文章

  1. cocos2d 中使用jni Java 调用 C++ 方法

    1.首先是LoadLibrary cocos2d中的C++代码会编译成一个.so文件.放在安卓文件夹下的libs/armeabi 下,然后java会load进来,这步我们不用做了,由于cocos2d已 ...

  2. [JNI] Java 调用 C++ dll

    首先介绍一下JNI吧! JNI 是Java提供的一个用于调用本地接口的接口层,位于Java代码 和 本地代码之间的一层:主要功能是 数据类型的转换,还有就是通过这一层来调用本地代码! 下面就说说Jav ...

  3. Java 调用存储过程、函数

     一.Java调用存储Oracle存储过程 测试用表: --创建用户表 create table USERINFO ( username ) not null, password ) not null ...

  4. JNI Java调用C代码 示例

    Activity public class MainActivity extends ListActivity {     static {         System.loadLibrary(&q ...

  5. java调用存储过程和函数

    以对表test进行增,删,改,查进行说明:1.新建表test create table TEST ( TID NUMBER not null, TNAME VARCHAR2(32), TCODE VA ...

  6. 【转】java调用存储过程和函数

    一.概述 如果想要执行存储过程,我们应该使用 CallableStatement 接口. CallableStatement 接口继承自PreparedStatement 接口.所以CallableS ...

  7. java 调用SAP RFC函数错误信息

    RFC接口调用SAP如果有异常会通过com.sap.mw.jco.JCO$Exception: 抛出异常 在开发中遇到的异常有如下 用户名密码可能是错误或者用户无权限,确认用户,必要时联系SAP负责人 ...

  8. JNI——JAVA调用C

    1. 编译java:javac JNIDemo.java 2. 编译JNI:gcc -I/usr/lib/jvm/java-1.8.0-openjdk-amd64/include/ -I/usr/li ...

  9. ubuntu下使用JNI Java调用C++的例子

    TestJNI.java public class TestJNI { static{ System.load("/home/buyizhiyou/workspace/JNI/src/lib ...

随机推荐

  1. SQL、Linq相关字段搜索

    结合一些分词组件,如盘古,对于用户查询关键字红按钮很容易分出 ‘红’ ‘按钮’二个单词 我们假设产品名称列里面是红色,规格里面是按钮 /* 普通sql实现全文搜索declare @key1 nvarc ...

  2. 阅读笔记—EL表达式

    表达式语言(EL) 表达式语言是一种在JSP页面中使用的数据访问语言,通过它可以很方便地在JSP页面中访问应用程序数据. 使用EL访问数据 表达式语言的使用形式:              ${exp ...

  3. Mysql学习总结(16)——Mysql之数据库设计规范

    一.三大范式 1.第一范式:消除一个字段包含多个数据库值,消除一个记录包含重复的组(单独的一列包含多个项目),即可满足1NF. 2.第二范式:消除部分依赖性即可转化为2NF.部分依赖性表示一个记录中包 ...

  4. 虚拟机上的Ubuntu开机显示“无法应用原保存的显示器配置”

    如图: 解决方法: 删除monitors.xml 文件 rm ~/.config/monitors.xml

  5. 使WordPress改域名后网站正常运行的方法

    使WordPress改域名后网站正常运行的方法 wp-content/wp-config.php $path = '/blog'; $scheme = (isset($_SERVER['HTTPS'] ...

  6. 关于搭建Session服务器(转载)

    Session服务器配置指南与使用经验 一.摘要 所有Web程序都会使用Session保存数据. 使用独立的Session服务器可以解决负载均衡场景中的Session共享问题.本文介绍.NET平台下建 ...

  7. 支付宝支付Java后台总结

    这个支付的流程是前端H5(APP等)需要支付时调用后台的接口拿到我们加密的签名去调起支付宝的支付界面(支付宝APP)进行支付操作,并且前端在支付成功后,支付宝后台会回调一个我们在签名时写入的一个接口地 ...

  8. ASP.NET MVC案例教程(基于ASP.NET MVC beta)——第四篇:传递表单数据

    摘要      本文将完成我们“MVC公告发布系统”的公告发布功能,以此展示在ASP.NET MVC中如何传递处理表单的数据. 前言      通过前几篇文章,我们已经能比较自如的使用ASP.NET ...

  9. 【例 7-12 UVA - 1343】The Rotation Game

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 迭代加深搜索. 每次抽动操作最多只会让中间那一块的区域离目标的"距离"减少1. 以这个作为剪枝. 枚举最大深度. ...

  10. amazeui学习笔记--css(HTML元素3)--表单Form

    amazeui学习笔记--css(HTML元素3)--表单Form 一.总结 1.form样式使用:在容器上添加 .am-form class,容器里的子元素才会应用 Amaze UI 定义的样式. ...