• 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与系统中的原生方法交互的技术(在windowslinux系统中,实现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代码如下:

    package net.oseye.JniDemo;
    
    public class App 
    {
        public static void main( String[] args )
        {
        	//调用native方法
        	new Hello().sayHello();
        }
    }
    
    class Hello{
    	static{
    		System.loadLibrary("libhello");
    	}
    	
    	/*
    	 * 声明native方法
    	 */
    	public native void sayHello();
    }

    编译后会生成App.class和Hello.class。


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

    使用命令

    D:workspace4jeeJniDemo	argetclasses>javah -jni net.oseye.JniDemo.Hello

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

    net_oseye_JniDemo_Hello.h代码:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class net_oseye_JniDemo_Hello */
    
    #ifndef _Included_net_oseye_JniDemo_Hello
    #define _Included_net_oseye_JniDemo_Hello
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     net_oseye_JniDemo_Hello
     * Method:    sayHello
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_net_oseye_JniDemo_Hello_sayHello
      (JNIEnv *, jobject);
    
    #ifdef __cplusplus
    }
    #endif
    #endif

    使用c/c++实现native方法

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

    #include <stdio.h>
    #include "net_oseye_JniDemo_Hello.h"
    
    JNIEXPORT void JNICALL Java_net_oseye_JniDemo_Hello_sayHello
      (JNIEnv *, jobject)
    {
    	printf("Hello, world
    ");
    }

    使用gcc编译成dll,命令:

    D:workspace4jeeJniDemo	argetclasses>gcc -shared -Wl,--kill-at -I "d:Program FilesJavajdk1.7.0_05include" hello.cpp -o libhello.dll

    此时我的D:workspace4jeeJniDemo argetclasses路径结构如下:

    执行Java程序

    D:workspace4jeeJniDemo	argetclasses>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类型
      本地类型
      描述
      boolean
      jboolean
      C/C++8位整型
      byte
      jbyte
      C/C++带符号的8位整型
      char
      jchar
      C/C++无符号的16位整型
      short
      jshort
      C/C++带符号的16位整型
      int
      jint
      C/C++带符号的32位整型
      long
      jlong
      C/C++带符号的64位整型
      float
      jfloat
      C/C++32位浮点型
      double
      jdouble
      C/C++64位浮点型
      Object
      jobject
      任何Java对象,或者没有对应java类型的对象
      Class
      jclass
      Class对象
      String
      jstring
      字符串对象
      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种方法: 
        gcc -Wl,--kill-at -shared -o jnihello.dll Native.c
        这种方法生成不带@的函数声明
      2. 第2种方法:
        gcc -Wl,--add-stdcall-alias -shared -o jnihello22.dll Native.c 
        这种方法会生成2个函数声明,一个是带@的 一个是不带@的。
      3. 第3种方法: 在你的本地方法的头文件中中的函数前面加上下划线,比如以前是 
        JNIEXPORT void JNICALL Java_net_oseye_JniDemo_Hello_sayHello(JNIEnv *, jobject);
        现在改成 
        JNIEXPORT void JNICALL _Java_net_oseye_JniDemo_Hello_sayHello(JNIEnv *, jobject);
        同时你的实现的cpp文件或者c文件里的函数头也要一致 前面有下划线。
    出处:http://www.zhaiqianfeng.com    
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    k8s1.13.0二进制部署-Dashboard和coredns(五)
    一、服务器简介
    Java检测端口的占用情况
    GreenMail邮件测试服务器
    PowerMock简单使用
    源码-集合:ArrayList
    Maven初步接触
    Java测试工具和框架
    Git初步
    java+搜索引擎
  • 原文地址:https://www.cnblogs.com/zhaiqianfeng/p/4621217.html
Copyright © 2020-2023  润新知