首先了解下,什么是JNI, JNI是Java Native Interface的缩写。从Java 1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。
这个文件是自动生成的,无需修改!这里的JNIEXPORT void JNICALL Java_j_1dll_sayHello
(JNIEnv *, jobject, jstring);方法就是按照java类中的sayHello方法生成的。
使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java 虚拟机实现下。
说白了就是让JAVA和其他语言开的东西进行交互,这里通过一个实例着重介绍下,如何通过JNI使JAVA可以调用本地的DLL。
实例描述:假定现在有一个现成的DLL文件,(这种情况在项目开发中很容易遇到,比如说客户方原来有个老系统,是用C/C++等来开发的,一堆写好的DLL,而如果在功能需求方面没有变化的提前下,利用这些DLL能给项目省下不少工夫),DLL的源文件已经找不到了,通过dump查看只有一个sayHello方法,输入一个名字,如Tom,输出Hello Tom!(当然为了实现这个例子,这个DLL还是要先写出来,但现实中,这个DLL往往是现成了,无需改变,也没法改变)。目前的需求是这样的,在JAVA程序实现:输入一个Tom,调用这个DLL,输出Hello Tom!
实现思路:众所周知,JAVA是不能直接调用DLL文件,不过可以通过JNI,生成一个四不像的DLL,这个DLL既能用JAVA语法,又能与C/C++开发出来的DLL进行交互。于是我们可以通过JAVA调用这个四不像的DLL1,然后在这个DLL1中调用现成的DLL2,实现整个过程,如下图:
具体实现:
1。写一个简单的C++程序,生成那个现成的DLL2,名为c_dll.cpp,实现sayHello这个方法,代码如下:
#include
<
objbase.h
>
#include < iostream.h >
extern " C " __declspec(dllexport) void sayHello ( char * name)
... {
cout<<"Hello "<<name<<endl;
}
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void * lpReserved)
... {
HANDLE g_hModule;
switch(dwReason)
...{
case DLL_PROCESS_ATTACH:
g_hModule = (HINSTANCE)hModule;
break;
case DLL_PROCESS_DETACH:
g_hModule=NULL;
break;
}
return TRUE;
}
#include < iostream.h >
extern " C " __declspec(dllexport) void sayHello ( char * name)
... {
cout<<"Hello "<<name<<endl;
}
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void * lpReserved)
... {
HANDLE g_hModule;
switch(dwReason)
...{
case DLL_PROCESS_ATTACH:
g_hModule = (HINSTANCE)hModule;
break;
case DLL_PROCESS_DETACH:
g_hModule=NULL;
break;
}
return TRUE;
}
通过命令或IDE将其编译成DLL文件,如命令提示符下:
cl
/
c c_dll.cpp
link / dll c_dll.obj
link / dll c_dll.obj
生成dll文件c_dll.dll。
2。写一个简单的java类,声明一个由native修饰的方法sayHello(),代码如下:
public
class
j_dll
...
{
public native void sayHello(String name);
public static void main(String[] args) ...{
// TODO Auto-generated method stub
}
}
public native void sayHello(String name);
public static void main(String[] args) ...{
// TODO Auto-generated method stub
}
}
这里native修饰的sayHello方法就是java调用本地dll的程序入口。
3。通过JNI将这个java类编译成充当桥梁作用的DLL1,具体步骤如下:
1)先用命令生成头文件j_dll.h,命令提示符下进入java类的目录,执行:
javac j_dll.java
javah j_dll
javah j_dll
就会生成一个名为j_dll.h的头文件,这个文件对于生成DLL1来说是必需的。j_dll.h如下:
/**/
/* DO NOT EDIT THIS FILE - it is machine generated */
#include < jni.h >
/**/ /* Header for class j_dll */
#ifndef _Included_j_dll
#define _Included_j_dll
#ifdef __cplusplus
extern " C " ... {
#endif
/**//*
* Class: j_dll
* Method: sayHello
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_j_1dll_sayHello
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
#include < jni.h >
/**/ /* Header for class j_dll */
#ifndef _Included_j_dll
#define _Included_j_dll
#ifdef __cplusplus
extern " C " ... {
#endif
/**//*
* Class: j_dll
* Method: sayHello
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_j_1dll_sayHello
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
(JNIEnv *, jobject, jstring);方法就是按照java类中的sayHello方法生成的。
2)生成DLL1,新建一个名为j_dll.cpp的文件,代码如下:
#include
"
stdio.h
"
#include " j_dll.h "
#include < windows.h >
#include < iostream.h >
// 加载dll
HINSTANCE hinst = NULL;
JNIEXPORT void JNICALL Java_j_1dll_sayHello(JNIEnv * env, jobject obj, jstring s)
... {
//定义一个函数指针
typedef void (* sayHello )(char * name);
//定义一个函数指针变量
sayHello sh = NULL;
if (NULL == hinst)
...{
hinst=::LoadLibrary("c_dll.dll");
}
//找到dll的sayHello函数
sh =(sayHello)GetProcAddress(hinst, "sayHello");
//调用dll里的函数
try...{
if (NULL != sh)
...{
char *str;
str = (char *)env->GetStringUTFChars(s,0);
(*sh)(str);
env->ReleaseStringUTFChars(s,str);
}
FreeLibrary(hinst);
}catch(...)...{
cout<<"exception"<<endl;
}
}
#include " j_dll.h "
#include < windows.h >
#include < iostream.h >
// 加载dll
HINSTANCE hinst = NULL;
JNIEXPORT void JNICALL Java_j_1dll_sayHello(JNIEnv * env, jobject obj, jstring s)
... {
//定义一个函数指针
typedef void (* sayHello )(char * name);
//定义一个函数指针变量
sayHello sh = NULL;
if (NULL == hinst)
...{
hinst=::LoadLibrary("c_dll.dll");
}
//找到dll的sayHello函数
sh =(sayHello)GetProcAddress(hinst, "sayHello");
//调用dll里的函数
try...{
if (NULL != sh)
...{
char *str;
str = (char *)env->GetStringUTFChars(s,0);
(*sh)(str);
env->ReleaseStringUTFChars(s,str);
}
FreeLibrary(hinst);
}catch(...)...{
cout<<"exception"<<endl;
}
}
注意开始要引入进来j_dll.h文件,关键代码有写注释,然后按照生成DLL2的方法生成DLL1,名为j_dll.dll(具体方法略)。
4。补全j_dll.java的代码,如下:
public
class
j_dll
...
{
// Java调用dll
static ...{
try ...{
System.loadLibrary("j_dll");
} catch (Error e) ...{
System.exit(0);
}
}
public native void sayHello(String name);
public static void main(String[] args) ...{
// TODO Auto-generated method stub
j_dll jd = new j_dll();
jd.sayHello("TOM");
}
}
// Java调用dll
static ...{
try ...{
System.loadLibrary("j_dll");
} catch (Error e) ...{
System.exit(0);
}
}
public native void sayHello(String name);
public static void main(String[] args) ...{
// TODO Auto-generated method stub
j_dll jd = new j_dll();
jd.sayHello("TOM");
}
}
编译java类,运行之,输出Hello Tom。
这样,整个JAVA调用中间层DLL,中间层DLL调用现成的DLL就完成了,是不是很简单呢?希望喜欢JAVA的朋友一起交流哦!