JNI简介

JNI 简介 -----Java 调用 C 函数

Java 是面向对象的王牌语言,安全,规范,容易使用和“一次编译,到处运行”等等优点,很快让 Java 成为了世界第一语言。在很多方面, Java 都是无可挑剔的,但当涉及到某个平台的底层时,或需要深入某个操作系统时, Java 就显得力不从心了。幸运的是, Java 提供了调用本地方法的方式,通过 JNI ,可以让 Java 调用C 语言函数。这为我们提供了很多便利,比如,我们有现成的 C 语言库,不想用 Java 再重写,这时就可以通过JNI 来实现代码重用。但更重要的,我认为, JNI 可以拓展 Java ,让其完成更多更复杂的操作系统相关的任务。对于 JNI , 最常用的应用是用其实现与操作系统密切相关的任务。

JNI 与平常的接口过程没有二样儿,也是二个过程:定义接口和实现接口。只不过接口的定义是在 Java 层,而实现却是在 C 。这里要注意,虽然,也可以用 C++ 来实现接口,但是 JNI 只支持标准 C ,对 C++ 的类和STL ,模板和重载等不提供支持,所以当用 C++ 来实现时,请加上宏 ’extern “C” { }; , 以去掉那些 C 不支持的C++ 特性。

JNI 通常要四个步骤:

1. 定义接口

2. 用 C/C++ 实现接口

3. 把 C/C++ 的实现做成共享库 (Windows 平台的 .dll , Linux 平台的 .so)

4. 集成

下面我们通过一个具体的真正的实例来演示如何实现一个 JNI 。

问题: 虽然 Java 可以“一次编译,到处运行”,但是由于它对跨平台支持的太好了,所以用 Java 很难分辨出平台之间的差异。比如,我们如果想为 64 位的 Windows 和 32 的 Windows 做点不同的东西,那么就需要我们用程序来区分这二个系统,以进行不同的处理。 Java 里面提供了 os.arch 属性,但是这个是针对处理器的,并不是操作系统的。因为有些情况是在 32 位处理器上可能装 64 位的系统,在 64 位处理器也可装 32 位的系统,而我们想要的是操作系统是 64 位还是 32 位,并非处理器。这时就要求助于 JNI 了,用 C 语言通过 Windows 自身的 API 我们是可以很容易得出这个操作系统是 64 位还是 32 位。

实现过程:

1.  定义接口: 可以用如下代码来定义 SystemType.java

             

  1. public class SystemType {  
  2.         public native boolean is64BitSystem();  
  3.         static {  
  4.                System.loadLibrary(“SystemType”);  
  5.         }  
  6. }   

              在这里, public native boolean is64BitSystem(); 就是我们在 Java 层的接口,其他地方可以调用这个接口。下在的语句,是用来告诉 JVM 到哪个库里面去找接口的实现,另外我们还要告诉 JVM ,这个库是放在什么地方(路径),请参见 4 、集成。

2.  实现: 这是 JNI 中最关键的,能否完成就在此了。对于 Windows 操作系统的类型( 32-64 ),即使是用 C 也不是很容易的,因为很多常规方法都没有用。通过查找资料我找到了如下方法,通过验证,发现很有用 .

在实现的时候要注意,我们不能对 Java 接口直接进行实现,因为毕竟是二种不同的语言。 C 语言只能实现它自己的接口 ----- 头文件。为此我们必须要把 Java 的接口转化为 C 语言的头文件。如下便可实现:

javac SystemType.java

javah SystemType

如果没有任何错误就会生成一个头文件 SystemType.h ,这就是我们要实现的。注意,如果你的 Java 类是在某个包下面,那么在编译是要注意,一定要在根目录下,用完整的包名来编译,否则会出错。如:

SystemType.java

package util.system;

Public class SystemType { …. }

#pwd

/work/src/util/system

#cd /work/src

#javac util/system/SystemType.java

#javah util.system.SystemType

生成的头文件会是这相样子:

SystemType.h

            

  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class system_type_SystemType */  
  4.    
  5. #ifndef _Included_system_type_SystemType  
  6. #define _Included_system_type_SystemType  
  7. #ifdef __cplusplus  
  8. extern "C" {  
  9. #endif  
  10. /* 
  11. * Class:     system_type_SystemType 
  12. * Method:    is64BitWindows 
  13. * Signature: ()Z 
  14. */  
  15. JNIEXPORT jboolean JNICALL Java_system_1type_SystemType_is64BitWindows  
  16.            (JNIEnv *, jobject);  
  17.    
  18. #ifdef __cplusplus  
  19. }  
  20. #endif  
  21. #endif   

SystemType.c

            

  1. #include "stdafx.h"  
  2. #include "comutil.h"  
  3. #include "system_type_SystemType.h"  
  4. #include <cstdio>  
  5. #include <Shlobj.h>  
  6. #include <Shfolder.h>  
  7. #include <cerrno>  
  8. extern "C" {  
  9. #ifdef _MANAGED  
  10. #pragma managed(push, off)  
  11. #endif  
  12.    
  13. typedef BOOL (WINAPI *IW64PFP)(HANDLE, BOOL *);  
  14.    
  15. BOOL APIENTRY DllMain( HMODULE hModule,  
  16.                        DWORD  ul_reason_for_call,  
  17.                        LPVOID lpReserved  
  18.                                          )  
  19. {  
  20.     return TRUE;  
  21. }  
  22.    
  23. JNIEXPORT jboolean JNICALL Java_system_1type_SystemType_is64BitWindows  
  24. (JNIEnv *, jobject) {  
  25.    
  26. int res = false;  
  27. /* 
  28.   * On Windows, we need to open the log in current user's Application Data in which 
  29.   * have the full accesses to write. 
  30.   */  
  31.    
  32. IW64PFP IW64P = (IW64PFP)GetProcAddress(  
  33.            GetModuleHandle(L"kernel32"), "IsWow64Process");  
  34.    
  35. if(IW64P != NULL){  
  36.            IW64P(GetCurrentProcess(), &res);  
  37. }  
  38. return res;  
  39. }  
  40.    
  41. #ifdef _MANAGED  
  42. #pragma managed(pop)  
  43. #endif  
  44. }  

3. 编译共享库:

把 Visual Studio 的项目属性改为 dll, 然后编译,还要在 extra include files 选项中把 JDK 提供的 JNI 头文件加上 (jdk /include, jdk /include/win32) ,就得到了我们想要的东西了,剩下的任务就是把这些东西集成起来,让他们工作。

注意:如果是在 Linux 平台下面,则要加上一些选项:

gcc –fPIC –Ijdk /include –Ijdk /include/linux –shared –l libSystemType.so SystemType.c

4.  集成: 最后一道工序了,前面提到了,代码中的 System.loadLibrary 是告诉 JVM 到哪个库中去找接口的实现,而在运行的时候要给告诉 JVM ,这个库文件放在哪。因此我们要给 JVM 加上一个参数如

java -Djava.library.path=D:/workspace/MyProject/libs

然后把库放到 D:/workspace/MyProject/libs 就可以了。

这样,一个简单的 JNI 就完成了,当然,还可以用 JNI 实现更复杂的功能。比如可以在 JNI 的 C 实现中使用一些 Java 特性,如抛出异常,还可以使用 Java 的 API 等等,更详细的请参见《 Java 核心编程,卷二,第十一章:本地方法》 .

参考资料: Car S. Horstman, Gary Cornell 《 Java 核心技术,卷二:高级特性》第七版,十一章, 2008

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值