JNI之C初探

JNI是Java Native Interface的缩写,从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。

目前java与dll交互的技术主要有3种:jni,jawin和jacob。Jni(Java Native Interface)是sun提供的java与系统中的原生方法交互的技术(在windows\linux系统中,实现java与native method互调)。目前只能由c/c++实现。后两个都是sourceforge上的开源项目,同时也都是基于jni技术的windows系统上的一个应用库。Jacob(Java-Com Bridge)提供了java程序调用microsoft的com对象中的方法的能力。而除了com对象外,jawin(Java/Win32 integration project)还可以win32-dll动态链接库中的方法。就功能而言:jni >> jawin>jacob,就易用性而言,正好相反:jacob>jawin>>jni。

Jni程序开发的一般操作步骤如下
  1. 编写java类声明native方法;
  2. 用javah生成c/c++原生函数的头文件;
  3. 编写c/c++代码实现原生函数并编译成库(windows是dll,linux是so);
  4. 通过System.loadLibrary()或System.load()加载生成的库,或则给虚拟机传参(java.library.path)指定库的路径;
  5. java调用native方法进行业务处理;

下面我们按部就班地进行操作(windows下),编写java类声明native方法

项目结构如下如下:

App.java代码如下:


  
  
  1. package net.oseye.JniDemo;
  2.  
  3. public class App
  4. {
  5. public static void main( String[] args )
  6. {
  7. //调用native方法
  8. new Hello().sayHello();
  9. }
  10. }
  11.  
  12. class Hello{
  13. static{
  14. System.loadLibrary("libhello");
  15. }
  16. /*
  17. * 声明native方法
  18. */
  19. public native void sayHello();
  20. }
编译后会生成App.class和Hello.class。


用javah生成c/c++原生函数的头文件

使用命令

D:\workspace4jee\JniDemo\target\classes>javah -jni net.oseye.JniDemo.Hello

生成c/c++头文件 net_oseye_JniDemo_Hello.h:

net_oseye_JniDemo_Hello.h代码:


  
  
  1. /* DO NOT EDIT THIS FILE - it is machine generated */
  2. #include <jni.h>
  3. /* Header for class net_oseye_JniDemo_Hello */
  4.  
  5. #ifndef _Included_net_oseye_JniDemo_Hello
  6. #define _Included_net_oseye_JniDemo_Hello
  7. #ifdef __cplusplus
  8. extern "C" {
  9. #endif
  10. /*
  11. * Class: net_oseye_JniDemo_Hello
  12. * Method: sayHello
  13. * Signature: ()V
  14. */
  15. JNIEXPORT void JNICALL Java_net_oseye_JniDemo_Hello_sayHello
  16. (JNIEnv *, jobject);
  17.  
  18. #ifdef __cplusplus
  19. }
  20. #endif
  21. #endif
使用c/c++实现native方法

在与net_oseye_JniDemo_Hello.h的目录下建立hello.cpp,代码:


  
  
  1. #include <stdio.h>
  2. #include "net_oseye_JniDemo_Hello.h"
  3.  
  4. JNIEXPORT void JNICALL Java_net_oseye_JniDemo_Hello_sayHello
  5. (JNIEnv *, jobject)
  6. {
  7. printf("Hello, world\n");
  8. }
使用gcc编译成dll,命令:

D:\workspace4jee\JniDemo\target\classes>gcc -shared -Wl,--kill-at -I "d:\Program Files\Java\jdk1.7.0_05\include" hello.cpp -o libhello.dll

此时我的D:\workspace4jee\JniDemo\target\classes路径结构如下:

执行Java程序

D:\workspace4jee\JniDemo\target\classes>java -Djava.library.path=. net.oseye.JniDemo.App

输出

Hello, world


以备不时之需的PS:

  1. linux下有非常好用的c/c++编译器gcc,windows下也有移植,貌似大家比较喜欢MinGW,但需要在线安装,因此需要访问公网权限。我使用了TDM-GCC,它可离线安装,它结合了 GCC 工具集中最新的稳定发行版本,包括了自由并开源的 MinGW 或 MinGW-w64 的运行时 APIs,以此创建一个 LIBRE 来替代微软的编译器及其平台 SDK。GCC简单使用教程可查看百度文库
  2. JNI基本类型
    Java类型
    本地类型
    描述
    booleanjbooleanC/C++8位整型
    bytejbyteC/C++带符号的8位整型
    charjcharC/C++无符号的16位整型
    shortjshortC/C++带符号的16位整型
    intjintC/C++带符号的32位整型
    longjlongC/C++带符号的64位整型
    floatjfloatC/C++32位浮点型
    doublejdoubleC/C++64位浮点型
    Objectjobject任何Java对象,或者没有对应java类型的对象
    ClassjclassClass对象
    Stringjstring字符串对象
    Object[]jobjectArray任何对象的数组
    boolean[]jbooleanArray布尔型数组
    byte[]jbyteArray比特型数组
    char[]jcharArray字符型数组
    short[]jshortArray短整型数组
    int[]jintArray整型数组
    long[]jlongArray长整型数组
    float[]jfloatArray浮点型数组
    double[]jdoubleArray双浮点型数组
  3. error: parameter name omitted
    如果你用c实现javah生成的头文件,可能会遇到这个这个问题,这是由于C与C++的细微区别造成的:
    • 在函数声明中:无论是C还是在C++,都可以省略形式参数名。但是,通常都不建议省略形式参数名.
    • 在函数实现中:
      1. 当需要使用形式参数的时候,显然,必须给形式参数命名。
      2. 当不需要使用形式参数的时候,C与C++有微小差异:
        C不能省略形式参数名, 即使不使用。
        C++可以省略形式参数名,如果不使用。并且在C++中,如果给不使用的形式参数命名,可能会得到一个警告。
    由于使用javah生成的头文件是省略形参的,如果你直接拷贝函数定义到实现中,而源文件保存成c而非cpp,就会出现这个错。
  4. java.lang.Unsatisfie.lang.UnsatisfiedLinkError  no XXXXX in java.library.path
    报这个异常主要是找不到你的库(dll或so)文件,你可以使用-Djava.library.path指定库文件地址,或者你通过System.getProperty("java.library.path")获取默认java.library.path地址,把库文件拷贝到里面去。
  5. java.lang.UnsatisfiedLinkError: XXXclass.XXXmethod()
    这个错误是 在这个dll里找不到方法的声明,网上说是@符号的问题 ,主要有三种解决方法:
    1. 第1种方法: 
           
           
      1. gcc -Wl,--kill-at -shared -o jnihello.dll Native.c
      这种方法生成不带@的函数声明
    2. 第2种方法:
           
           
      1. gcc -Wl,--add-stdcall-alias -shared -o jnihello22.dll Native.c
      这种方法会生成2个函数声明,一个是带@的 一个是不带@的。
    3. 第3种方法: 在你的本地方法的头文件中中的函数前面加上下划线,比如以前是 
      
            
            
      1. JNIEXPORT void JNICALL Java_net_oseye_JniDemo_Hello_sayHello(JNIEnv *, jobject);
      现在改成 
      
            
            
      1. JNIEXPORT void JNICALL _Java_net_oseye_JniDemo_Hello_sayHello(JNIEnv *, jobject);
      同时你的实现的cpp文件或者c文件里的函数头也要一致 前面有下划线。



参考地址:http://www.oseye.net/user/kevin/blog/310
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值