在实际应用中,经常会有下面的情况。已经给出了由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,如有不正确的地方,还望各位大牛不吝赐教,予以指正!