java调用c 动态库设置句柄_linux下 java JNI调用C语言动态链接库 | 学步园

本文详细介绍了在Linux环境下,如何使用Java JNI调用C语言编写的动态链接库。通过创建Java类,定义本地方法,生成头文件,编写C代码并编译成动态库,最后在Java程序中加载并测试本地方法。
摘要由CSDN通过智能技术生成

JNI是Java native interface的简写,可以译作Java原生接口。Java可以通过JNI调用C/C++的库,这对于那些对性能要求比较高的Java程序无疑是一个福音。

使用JNI也是有代价。大家都知道JAVA程序是运行在JVM之上的,可以做到平台无关。但是如果Java程序通过JNI调用了原生的代码(比如c/c++等),则Java程序就丧失了平台无关性。最起码需要重新编译原生代码部分。所以应用JNI需要好好权衡,不到万不得已,请不要选择JNI,可以选择替代方案,比如TCP/IP进行进程间通讯等等。这也是为什么谷歌的Android平台的底层虽然用JNI实现,但是他不建议开发人员用JNI来开发Android上面的应用的原因。将会丧失Android上面的应用程序平台无关性。

下面是在linux下java jni调用C语言动态链接库的具体操作步骤。

1、创建一个Java程序(Hello.java)定义原生的c/c++函数。

2、用javac编译Hello.java生成Hello.class。

3、用javah带-jni参数编译Hello.class生成Hello.h文件,该文件中定义了c的函数原型。在实现c函数的时候需要。

4、创建Hello.c,实现Hello.h定义的函数。

5、编译Hello.c生成libHello.so。

6、在java虚拟机运行java程序Hello。

第一步,定义一个Java类-- Hello.它提供SayHello方法:

此时应注意两点:

1.为要使用的每个本地方法编写本地方法声明,其声明方式与普通Java方法接口没什么不同,只是必须指定native关键字,如下所示:

public native void SayHello(String strName);

在这个函数中,我们将根据传进的人名,向某人问好。

2.必须显式地加载本地代码库。当然要调用System.loadLibrary("hello");注意此时不要lib,也不要.so!;我们需在类的一个静态块中加载这个库:

static

{

System.loadLibrary("hello");

}

再加上必要的异常处理就生成如下源文件Hello.java:

public class Hello

{

static

{

System.loadLibrary("hello");

}

//声明的本地方法

publicstaitc native void sayHello(String strName);

}

运行命令javac Hello.java生成Hello.class文件。

第二步,生成本地链接库。具体过程如下:

1.要为以上定义的类生成Java本地接口头文件,需使用javah,Java编译器的javah功能将根据Hello类生成必要的声明,此命令将生成Hello.h文件,我们在共享库的代码中要包含它,javah不使默认内部命令,需要指明路径,它在JDK的bin目录下,在我的Linux环境下命令如下:

javah Hello

但是出现如下错误:

error: cannot access Hello

class file for Hello not found

javadoc: error - Class Hello not found.

Error: No classes were specified on the command line.Try -help.

原因是CLASS_PATH没有把当前目录加入其中。所以必须指定classpath为当前目录。或者在系统CLASS_PATH加入当前路径。执行如下命令:

javah -classpath . Hello

生成的Hello.h文件内容的第一句子为#include 但是gcc里面默认环境可不知道jni.h是什么东西,jni.h在jdk的$JAVA_HOME/include下面,可进去查看一下~

2.在与Hello.h相同的路径下创建一个CPP文件Hello.cpp。注意,自动生成的那个函数名字很长,并且开头的Java是大写的,大小写很致命一定要注意。内容如下:

#include "Hello.h"

#include

//与Hello.h中函数声明相同

JNIEXPORT void JNICALL Java_Hello_sayHello(JNIEnv * env, jclass arg, jstring instring)

{

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

const jbyte *str =

(const jbyte *)env->GetStringUTFChars( instring, JNI_FALSE );

printf("Hello,%s/n",str);

//通知虚拟机本地代码不再需要通过str访问Java字符串。

env->ReleaseStringUTFChars( instring, (const char *)str );

return;

}

所有的JNI调用都使用了JNIEnv *类型的指针,习惯上在CPP文件中将这个变量定义为env,它是任意一个本地方法的第一个参数。env指针指向一个函数指针表,在VC中可以直接用"->"操作符访问其中的函数。

jobject指向在此Java代码中实例化的Java对象LocalFunction的一个句柄,相当于this指针。

后续的参数就是本地调用中有Java程序传进的参数,本例中只有一个String型参数。对于字符串型参数,因为在本地代码中不能直接读取Java字符串,而必须将其转换为C /C++字符串或Unicode。以下是三个我们经常会用到的字符串类型处理的函数:

const char* GetStringUTFChars(jstring string,jboolean* isCopy)

3.编译生成共享库。

使用GCC时,必须通知编译器在何处查找此Java本地方法的支持文件,并且显式通知编译器生成位置无关的代码,在我的环境中按如下过程编译:

g++ -I /usr/lib/jvm/java-6-sun/include/linux/ -I /usr/lib/jvm/java-6-sun/include/ -fPIC -c Hello.cpp

生成Hello.o

g++ -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0 Hello.o

生成libhello.so.1.0

接下来将生成的共享库拷贝为标准文件名

cp libhello.so.1.0 libhello.so

或者使用:

g++ -I /usr/lib/jvm/java-6-sun/include/linux/ -I /usr/lib/jvm/java-6-sun/include/ -fPIC -shared -o libLexical.so Lexical.cpp

注意在linux下,动态链接库的名字必须是lib****.so,必须以lib开头!4.编写一个简单的Java程序来测试我们的本地方法。

将如下源码存为ToSay.java:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值