在实际应用中,经常会有下面的情况。已经给出了由C/C++编译好的第三方动态链接库.dll文件,里面封装了一些我们要用的接口,并给出了接口相关的头文件,从中我们知道这些接口的参数类型和返回值类型,以方便我们调用这些接口。在Java应用中,有时为了某种需要,我们就必须要借助C/C++的动态链接库帮助我们完成我们要做的工作。这时JNI出现了,JNI(Java Native Interface)是由SUN提供的一套标准接口,供本地应用(C/C++)与JavaVM相互调用。
接下来就通过一个具体实例讲解JNI调用第三方动态库的用法。
一、准备工作:
由于手里面没有合适的第三方动态库可供调用,就自己手动封装了一些简单的函数接口,然后编译成动态链接库。实际应用中可能会提供已存在的动态链接库。下面就首先在VC中新建一个Win32项目BasicFunc——下一步——选择.dll 空项目 完成;新建头文件BasicFunc.h和源文件BasicFunc.cpp如下:
//BasicFunc.h:
double add(double a, double b);
double multiply(double a, double b);
double max(double a, double b);
//BasicFunc.cpp
#include <iostream>
using namespace std;
#ifdef __cplusplus
extern "C" {
#endif
//__declspec(dllexport) is used to force generate function name as 'add
//so you can find the address of this function by searching in the dll
__declspec(dllexport)double add(double a, double b)
{
cout<< a << "与" << b<< "的和是:" << a + b << endl;
returna + b;
}
__declspec(dllexport)double multiply(double a, double b)
{
cout<< a << "与" << b<< "的积是:" << a * b << endl;
returna*b;
}
__declspec(dllexport)double max(double a, double b)
{
cout<< a << "与" << b<< "中较大者是:" << (a > b ? a : b)<< endl;
return(a> b ? a : b);
}
#ifdef __cplusplus
}
#endif
在VC中编译即可生成名为BasicFunc.dll的动态链接库。接下来我们就演示在Java中调用BasicFunc.dll中封装的接口的方法。
二、为了调用BasicFunc.dll中的接口函数,需要借助JNI来实现。在JNI中通过一个中介dll调用该接口。
1) 编写一个类,声明native方法
public class CallBasicFunc {
public native voidCallBasicFuncAdd(double a,double b);
public native doubleCallBasicFuncMul(double a,double b);
static{
System.loadLibrary("BasicFuncMediumDll");
}
}
2)编译生成.h文件
cmd到CallThirdParty.java目录下,执行以下几个命令,生成.h文件。(需要设定java环境变量)
第一步:javac CallBasicFunc.java 生成CallBasicFunc.class
第二步:Javah –jni CallBasicFunc生成CallBasicFunc.h头文件
内容如下:
/* DO NOT EDIT THISFILE - it is machine generated */
#include <jni.h>
/* Header for classCallBasicFunc */
#ifndef
#define _Included_CallBasicFunc
#ifdef __cplusplus
extern "C"
#endif
/*
* Class: CallBasicFunc
* Method: CallBasicFuncAdd
* Signature: (DD)V
*/
JNIEXPORT void JNICALL
JNIEnv *, jobject, jdouble, jdouble);
/*
* Class: CallBasicFunc
* Method: CallBasicFuncMul
* Signature: (DD)D
*/
JNIEXPORT jdouble JNICALL
JNIEnv *, jobject, jdouble, jdouble);
#ifdef __cplusplus
}
#endif
#endif
3)创建C/C++工程,工程名为BasicFuncMediumDll,将上一步生成的头文件复制并添加到当前工程,添加源文件CallBasicFunc.cpp,在其中实现CallBasicFuncAdd ()和CallBasicFuncMul()方法.内容如下:
#include<Windows.h>
#include<iostream>
#include"CallBasicFunc.h"
using namespace
#ifdef __cplusplus
extern "C"
#endif
typedef double (*ThirdPartyFunc)(double, double);
JNIEXPORT void JNICALL
(JNIEnv *env, jobject obj, jdouble arg_a, jdouble arg_b)
{
HMODULE dlh = NULL;
ThirdPartyFunc thirdPartyFunc=NULL;
if (!(dlh = LoadLibrary(L"BasicFunc.dll")))
{
"LoadLibrary() failed: %d\n", GetLastError());
}
double a = arg_a;
double b = arg_b;
if (!(thirdPartyFunc = (ThirdPartyFunc)GetProcAddress(dlh,"add")))
{
"GetProcAddress() failed: %d\n", GetLastError());
}
"a+b="<< (*thirdPartyFunc)(a, b) << endl;
if (!(thirdPartyFunc = (ThirdPartyFunc)GetProcAddress(dlh,"multiply")))
{
"GetProcAddress() failed: %d\n", GetLastError());
}
"a*b="<< (*thirdPartyFunc)(a, b) << endl;
if (!(thirdPartyFunc = (ThirdPartyFunc)GetProcAddress(dlh,"max")))
{
"GetProcAddress() failed: %d\n", GetLastError());
}
"Bigger of a and b is: "
//thirdPartyFunc= NULL;
}
JNIEXPORT jdouble JNICALL
(JNIEnv *env, jobject obj, jdouble arg_a, jdouble arg_b)
{
double a = arg_a;
double b = arg_b;
HMODULE dlh = NULL;
ThirdPartyFunc thirdPartyFunc = NULL;
if (!(dlh = LoadLibrary(L"BasicFunc.dll")))
{
"LoadLibrary() failed: %d\n", GetLastError());
}
jdouble
if (!(thirdPartyFunc = (ThirdPartyFunc)GetProcAddress(dlh,"multiply")))
{
"GetProcAddress() failed: %d\n", GetLastError());
}
result = (*thirdPartyFunc)(a, b);
"有返回值的本地方法a*b="
return
}
#ifdef __cplusplus
}
#endif
4)Build这个工程,生成BasicFuncMediumDll.dll动态链接库
5)编写测试java程序Test.java,调用以上生成的dll库。如下:
public class Test {
public static void main(String[]args) {
CallBasicFunccallThirdParty = new CallBasicFunc();
callThirdParty.CallBasicFuncAdd(10.3, 4.8);
//System.out.println("两数之和是:"+res);
System.out.println("Java中调用有返回值的本地方法"+callThirdParty.CallBasicFuncMul(34.2, 5.48));
}
}
输出结果:
10.3与4.8的和是:15.1
a+b=15.1
10.3与4.8的积是:49.44
a*b=49.44
10.3与4.8中较大者是:10.3
Bigger of a and b is: 10.3
34.2与5.48的积是:187.416
有返回值的本地方法a*b=187.416
Java中调用有返回值的本地方法187.41600000000003
这里没有演示C/C++调用Java的情况,C/C++调用Java即是在本地方法中访问Java类的成员属性和成员方法。这里需要进一步的了解JNI对操作属性和方法的用法。以后再进一步研究。
注意:没有配置环境变量的情况下,需要将第三方动态库copy到C/C++工程BasicFuncMediumDll的根目录下,将BasicFuncMediumDll.dll copy到windows的System32目录或JDK的bin目录下。
由于本人也是初次接触JNI,如有不正确的地方,还望各位大牛不吝赐教,予以指正!