当前位置 博文首页 > 外星喵的博客:Java本地方法调用

    外星喵的博客:Java本地方法调用

    作者:[db:作者] 时间:2021-07-12 15:38

    JNI介绍

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

    使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java 虚拟机实现下。

    JNI的使用场景

    1. Java应用需要与本地其它非Java应用交互
    2. 遇到性能瓶颈,通常本地代码执行效率要优于Java代码
    3. JDK提供的功能不足以帮我们实现需要的功能

    JNI的问题

    1. 因为JNI有一个Native这个特点,一点有项目用了JNI,也就说明这个项目基本不能跨平台了。
    2. JNI调用是相当慢的,在实际使用的之前一定要先想明白是否有这个必要。
    3. 因为C++和C这样的语言非常灵活,一不小心就容易出错,比如代码就没有写析构字符串释放内存,就存在内存泄漏的问题,对于java developer来说因为有了GC 垃圾回收机制,所以大多数人没有写析构函数这样的概念。所以JNI也会增加程序中的风险,增大程序的不稳定性

    JNI实战

    主要分为以下几个步骤

    • 编写带有native声明的方法的java类

    • 使用javac命令编译所编写的java类

    • 使用javah -jni 类的全限定名 生成扩展名为h的头文件

    • 使用C/C++实现本地方法

    • 将C/C++编译的文件生成动态连接库(dll文件)

    • 在java项目中引入生成的C/C++库文件

    1.编写java程序

    1.这里以HelloWorld为例,java代码如下:

    package com.aliencat.javabase.jni;
    
    public class HelloJni {
    
        public native String sayHello(String name);
    
        static{
            System.loadLibrary("DLL1");
        }
    
        public static void main(String[] args) {
            System.out.println(new HelloJni().sayHello("hello"));
        }
    }
    

    声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明改方法为 native的,并且不能实现。

    Load动态库:System.loadLibrary(“hello”);加载动态库(我们可以这样理解:我们的方法 displayHelloWorld()没有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是System.loadLibrary();的参数“hello”是动态库的名字。

    2.然后进行编译

    打开控制台,进入java目录下输入以下指令:

    javah -jni com.aliencat.javabase.jni.HelloJni

    即可生成一个一个头文件com_aliencat_javabase_jni_HelloJni.h,内容如下:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include "jni.h"
    /* Header for class com_aliencat_javabase_jni_HelloJni */
    
    #ifndef _Included_com_aliencat_javabase_jni_HelloJni
    #define _Included_com_aliencat_javabase_jni_HelloJni
    #ifdef __cplusplus
    extern "C" {
    #endif
    	/*
    	 * Class:     com_aliencat_javabase_jni_HelloJni
    	 * Method:    sayHello
    	 * Signature: (Ljava/lang/String;)V
    	 */
    	JNIEXPORT jstring JNICALL Java_com_aliencat_javabase_jni_HelloJni_sayHello
    	(JNIEnv*, jobject, jstring);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    
    

    这个h文件相当于我们在java里面的接口,这里声明了一个Java_com_aliencat_javabase_jni_HelloJni_sayHello
    (JNIEnv*, jobject, jstring);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致。其中jobject为java的this引用,而jstring即是传入的字符串参数。

    3.使用Visual Studio编写c/c++本地方法实现

    下载安装Visual studi

    下载地址:https://visualstudio.microsoft.com/zh-hant/vs/whatsnew/

    然后安装选择以下组件即可:

    在这里插入图片描述

    新建项目

    选择创建动态链接库

    在这里插入图片描述

    引入头文件

    JDK目录的include目录下有一个jni.h的文件,include的win32目录下有个jni_md.h文件,还有java类生成C头文件HelloJni.h,也一起拷贝到DLL工程的目录下。主要在目录的头文件上右键选择添加->现有项:

    在这里插入图片描述

    用visual studio编写C代码

    hello.cpp即为我创建的c++文件。

    本地方法和javah命令生成的头文件里面声明的方法名相同。
    代码如下:

    #include "pch.h"
    #include "com_aliencat_javabase_jni_HelloJni.h"
    #include <iostream>
    #include <stdio.h>
    
    
    JNIEXPORT jstring JNICALL Java_com_aliencat_javabase_jni_HelloJni_sayHello
    (JNIEnv* env, jobject, jstring jstr) {
    
        //将jstring类型转换成char*类型
        char* rtn = NULL;
        jclass clsstring = env->FindClass("java/lang/String");
        jstring strencode = env->NewStringUTF("GB2312");
        jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
        jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
        jsize alen = env->GetArrayLength(barr);
        jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
        if (alen > 0)
        {
            rtn = (char*)malloc(alen + 1); //new char[alen+1];  
            memcpy(rtn, ba, alen);
            rtn[alen] = 0;
        }
        env->ReleaseByteArrayElements(barr, ba, 0);
    
        printf("%s ", rtn);
        printf("this is C++ print");
        const char* str = "this is come from C++ ";
        return env->NewStringUTF(str);
    }
    

    4.生成动态库

    首先选中项目根目录后右键点击属性,选择配置管理器,配置如下:

    在这里插入图片描述

    选中项目根目录后右键点击生成即可,控制台输出如下即表示成功:

    在这里插入图片描述

    5.在java项目中引入本地库

    以idea为例:

    在这里插入图片描述

    选择你的vs编译输出的文件夹(看你的vs控制台输出的位置)即可,我的是在以下面路径下:

    ? 在这里插入图片描述

    ?

    6.运行程序 java HelloJni.

    加载本地库后即可使用

        static{
            System.loadLibrary("DLL1");
        }
    

    执行main方法后输出如下:

    this is come from C++ 
    hello this is C++ print
    

    JNI数据类型

    Java类型本地类型(JNI)描述
    boolean(布尔型)jboolean无符号8比特
    byte(字节型)jbyte有符号8比特
    char(字符型)jchar无符号16比特
    short(短整型)jshort有符号16比特
    int(整型)jint有符号32比特
    long(长整型)jlong有符号64比特
    float(浮点型)jfloat32比特
    double(双精度浮点型)jdouble64比特
    void(空型)voidN/A
    cs
    下一篇:没有了