JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java 1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。
使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java 虚拟机实现下。
第一个Helloworld例子:
注意:把dll 文件放在Path下面之后 需要重新启动eclipse,因为eclipse 会在加载的时候去读Path,之后你修改了Path,eclipse 不会重新读。
使用JNI 的两个弊端:
一个java掉C++代码查看cpu 使用情况的例子,完全按照上述的步骤:
1.首先声明navtive 方法
package cn.com.xinli; public class JNIDemo { public native void sayHello(); public static void main(String[] args) { } }
2. 时候javah 命令生成头文件,到class 目录下 javah cn.com.xinli.JNIDemo
/* DO NOT EDIT THIS FILE - it is machine generated */ #include "jni.h" /* Header for class cn_com_xinli_JNIDemo */ #ifndef _Included_cn_com_xinli_JNIDemo #define _Included_cn_com_xinli_JNIDemo #ifdef __cplusplus extern "C" { #endif /* * Class: cn_com_xinli_JNIDemo * Method: sayHello * Signature: ()V */ JNIEXPORT void JNICALL Java_cn_com_xinli_JNIDemo_sayHello (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
3.编写vc++源文件 也就是实现 Java_cn_com_xinli_JNIDemo_sayHello ,具体实现 是我在晚上拷贝的 打印cpu
的利用率,这里需要引入 3个头文件 一个是jni.h 在 (C:\Java\jdk1.5.0_07\include) 第2个是在 jni_md.h (C:\Java\jdk1.5.0_07\include\win32) 还有一个就是我们使用 javah 生成头文件
#include "cn_com_xinli_JNIDemo.h" #include <iostream> #include <windows.h> #include <conio.h> #include <stdio.h> using namespace std; #define SystemBasicInformation 0 #define SystemPerformanceInformation 2 #define SystemTimeInformation 3 #define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 + (double)((x).LowPart)) typedef struct { DWORD dwUnknown1; ULONG uKeMaximumIncrement; ULONG uPageSize; ULONG uMmNumberOfPhysicalPages; ULONG uMmLowestPhysicalPage; ULONG uMmHighestPhysicalPage; ULONG uAllocationGranularity; PVOID pLowestUserAddress; PVOID pMmHighestUserAddress; ULONG uKeActiveProcessors; BYTE bKeNumberProcessors; BYTE bUnknown2; WORD wUnknown3; } SYSTEM_BASIC_INFORMATION; typedef struct { LARGE_INTEGER liIdleTime; DWORD dwSpare[76]; } SYSTEM_PERFORMANCE_INFORMATION; typedef struct { LARGE_INTEGER liKeBootTime; LARGE_INTEGER liKeSystemTime; LARGE_INTEGER liExpTimeZoneBias; ULONG uCurrentTimeZoneId; DWORD dwReserved; } SYSTEM_TIME_INFORMATION; typedef LONG (WINAPI *PROCNTQSI)(UINT,PVOID,ULONG,PULONG); PROCNTQSI NtQuerySystemInformation; JNIEXPORT void JNICALL Java_cn_com_xinli_JNIDemo_sayHello (JNIEnv * env, jobject obj) { SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo; SYSTEM_TIME_INFORMATION SysTimeInfo; SYSTEM_BASIC_INFORMATION SysBaseInfo; double dbIdleTime; double dbSystemTime; LONG status; LARGE_INTEGER liOldIdleTime = {0,0}; LARGE_INTEGER liOldSystemTime = {0,0}; NtQuerySystemInformation = (PROCNTQSI)GetProcAddress(GetModuleHandle("ntdll"),"NtQuerySystemInformation"); if (!NtQuerySystemInformation) return; // get number of processors in the system status = NtQuerySystemInformation(SystemBasicInformation,&SysBaseInfo,sizeof(SysBaseInfo),NULL); if (status != NO_ERROR) return; cout<<"\nCPU Usage: "<<endl; while(!_kbhit()) { // get new system time status = NtQuerySystemInformation(SystemTimeInformation,&SysTimeInfo,sizeof(SysTimeInfo),0); if (status!=NO_ERROR) return; // get new CPU's idle time status =NtQuerySystemInformation(SystemPerformanceInformation,&SysPerfInfo,sizeof(SysPerfInfo),NULL); if (status != NO_ERROR) return; // if it's a first call - skip it if (liOldIdleTime.QuadPart != 0) { // CurrentValue = NewValue - OldValue dbIdleTime = Li2Double(SysPerfInfo.liIdleTime) - Li2Double(liOldIdleTime); dbSystemTime = Li2Double(SysTimeInfo.liKeSystemTime) - Li2Double(liOldSystemTime); // CurrentCpuIdle = IdleTime / SystemTime dbIdleTime = dbIdleTime / dbSystemTime; // CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors dbIdleTime = 100.0 - dbIdleTime * 100.0 / (double)SysBaseInfo.bKeNumberProcessors + 0.5; //printf("\b\b\b\b%3d%%",(UINT)dbIdleTime); cout<<(UINT)dbIdleTime<<endl; } // store new CPU's idle and system time liOldIdleTime = SysPerfInfo.liIdleTime; liOldSystemTime = SysTimeInfo.liKeSystemTime; // wait one second Sleep(1000); } //printf("\n"); cout<<"\n"<<endl; }
在吧 .cpp 文件 编译成动态链接库 的时候我使用了 Microsoft Visual C++ 6.0 ,这个东东好的大呀,真是心疼我的机器呀,整个项目的 结构如下图
然后需要我们将 .cpp的源文件 编译成dll
a. 在 工程-》设置 下面的 对象/库模块 下加上 ws2_32.lib
b. 输出加上 test.dll
编译,后在 D:\Program Files\Microsoft Visual Studio\MyProjects\test\Release 看到 test.dll
4. 写java 端调用代码
package cn.com.xinli; import java.util.Date; public class JNIDemo { public native void sayHello(); /** * @param args */ static { System.loadLibrary("test"); } public int property; public int function(int foo,Date date,int[] arr) { System.out.println("function"); return 0; } public static void main(String[] args) { System.out.println("系统Path路径:"+System.getProperty("java.library.path")); JNIDemo jNIDemo=new JNIDemo(); jNIDemo.sayHello(); } }
需要我们把 test.dll 放在打印出的path 路径下
运行成功!!!