从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. logout命令用于退出当前登录的Shell

    logout命令用于退出当前登录的Shell

  2. [PWA] Deal with caches in PWA

    The takeway is to know when we should cache the content? When we should clean the caches? 1. When sh ...

  3. [Angular] Create a custom validator for reactive forms in Angular

    Also check: directive for form validation User input validation is a core part of creating proper HT ...

  4. 使用框架的php假设使用定时服务Cronjob

    工作须要用php开发了个监控的小程序,既然是监控就须要定时运行. 之前我用的是chrome加个定时刷新的小插件,放在server上执行.也能实现,就是别扭. 通用正规的做法应该是:linux上的Cro ...

  5. Date类的用法

    package example; import java.text.DateFormat; import java.text.ParseException; import java.text.Simp ...

  6. C# 中编写函数式代码

    http://www.dotnetcurry.com/csharp/1384/functional-programming-fsharp-for-csharp-developers 写给 C# 开发人 ...

  7. jQuery post 传递 iframe

    //使用POST链接iframe function doOpenPostIfrm(url, args, iframe) { //创建一个隐藏表单 var _form = $("<for ...

  8. Codeforces 919F. A Game With Numbers(博弈论)

      Imagine that Alice is playing a card game with her friend Bob. They both have exactly 88 cards and ...

  9. Vue+TypeScript学习

    Vue CLI 内置了 TypeScript 工具支持.在 Vue 的下一个大版本 (3.x) 中也计划了相当多的 TypeScript 支持改进,包括内置的基于 class 的组件 API 和 TS ...

  10. 【例题 8-1 UVA 120 】Stacks of Flapjacks

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 从大到小安排. 显然想让第i大的数字归位 只要让他翻到最上面,然后再翻回来就ok了 即operate(pos[i]) -> o ...