从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. python学习日记-180823

    列表 a=[ ] 1.负数下标:a=[-1]指的是列表a最后一个下标,-2指倒数第二个下标 2.切片——利用切片获得子列表 a[1:4]——'1'切片开始处的下标,‘4’切片结束处的下标(不包括此下标 ...

  2. C#学习第一课

    C#和Java存在很多相似之处,但是也存在一些差异.今天下午刚去图书馆借了C#的入门书籍,进过简单的入门,我了解了几点不同之处: 1. Java中的基本数据类型只有8种,数据类型不存在无符号和有符号的 ...

  3. Lightoj 1127 - Funny Knapsack 【二分】

    题目链接:problem=1127">http://www.lightoj.com/volume_showproblem.php?problem=1127 题意:有n个物体(n< ...

  4. WINDOWS8.1企业版官方下载

    这是评估版 WINDOWS8.1企业版官方下载源 https://www.itechtics.com/download-windows-8-1-enterprise-offline-installer ...

  5. js21---单体(单例)模式

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...

  6. js03 数组

    变量的自动转换=== 等同符:不会发生类型的自动转化! == 等值符:会发生类型自动转化.自动匹配!判断相等没有equals()方法,只有2个等号3个等号. <!DOCTYPE HTML PUB ...

  7. CMake设置生成vs工程的动态库输出路径

    作者:朱金灿 来源:http://blog.csdn.net/clever101 在网上搜了很多的资料,发现CMake不能设置一个动态库工程的输出目录和中间目录,难道除了VC之外其它编译器如gcc中没 ...

  8. Fedora 10下应用网络模拟器NS心得

    650) this.width=650;" onclick='window.open("http://blog.51cto.com/viewpic.php?refimg=" ...

  9. 2018/8/21 qbxt测试

    2018/8/21 qbxt测试 期望得分:0? 实际得分:0 思路:manacher   会写模板但是不会用 qwq 听了某人的鬼话,直接输出0,然后就gg了 #include <cstdio ...

  10. php数组函数(分类基本数组函数,栈函数,队列)

    php数组函数(分类基本数组函数,栈函数,队列函数) 一.总结 1.常用数组函数 函数 描述 array() 创建数组. array_combine() 通过合并两个数组来创建一个新数组. array ...