Java的本地调用方式JNI和JNA的简单使用

Java的本地调用方式JNI和JNA的简单使用

admin 104 2024-01-05

JNI

步骤1:编写java代码

import java.util.Scanner;
public class HelloJNI { 
static { 
System.loadLibrary("HelloWorld"); 
} 
public native static void setNum(int num); 
public native static void setStr(String str); 
public native static int get();
public static void main(String[] args) { 
    HelloJNI test = new HelloJNI(); 
    test.setNum(666); 
    System.out.println(test.get());
	

        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("请输入字符串:");
            String str = scanner.nextLine();
            if (str.equals("quit")) {
                break;
            }
            test.setStr(str);
        }
       }
}

步骤2:编译成class文件

javac HelloJNI

步骤3:编译成HelloJNI.h文件

使用javah工具将.class文件编译成.h文件

javah -jni HelloJNI

编译后的.h文件中包含了java调用的c方法名,内容如下。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */

#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloJNI
 * Method:    setNum
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_HelloJNI_setNum
  (JNIEnv *, jclass, jint);

/*
 * Class:     HelloJNI
 * Method:    setStr
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_HelloJNI_setStr
  (JNIEnv *, jclass, jstring);

/*
 * Class:     HelloJNI
 * Method:    get
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_HelloJNI_get
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

步骤4:编写c的实现

#include "HelloJNI.h"
#include "stdio.h"
int result=888; 
JNIEXPORT void JNICALL Java_HelloJNI_setNum
  (JNIEnv * env, jclass jc, jint num){
    result+=num; 

  }

  JNIEXPORT jint JNICALL Java_HelloJNI_get
  (JNIEnv * env, jclass jc){
    return result;

  }


JNIEXPORT void JNICALL Java_HelloJNI_setStr
  (JNIEnv * env, jclass jc, jstring str){
  
    const char *c_str = env->GetStringUTFChars(str, NULL);
    if (c_str != NULL) {
        printf("C_Rx: %s\n", c_str);
        env->ReleaseStringUTFChars(str, c_str);
    }
  }

步骤5:收集文件

将步骤3生成的HelloJNI.h文件放在c文件的同级目录

步骤6:将cpp文件编译成dll文件

使用gcc将.c或.cpp文件编译成dll文件(windows平台),linux平台需要编译成.so文件。

gcc -shared -o 文件名.dll -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" c文件名.cpp 

JAVA_HOME是自己配置的环境变量,Java开发人员应该都懂。

需要注意的是linux平台下

  • %JAVA_HOME%需要换成$JAVA_HOME

  • %JAVA_HOME%\include\win32需要换成linux平台的$JAVA_HOME/include/linux

步骤7:移动DLL文件

将编译的dll文件放在 HelloJNI.class同级目录下

步骤8:运行

Java HelloJNI 

JNA

步骤1:确定c中定义的接口

dll.h头文件如下。

#ifndef _DLL_H_
#define _DLL_H_

#ifdef __cplusplus
extern "C" {
#endif

#if BUILDING_DLL
#define DLLIMPORT __declspec(dllexport)
#else
#define DLLIMPORT __declspec(dllimport)
#endif

DLLIMPORT void HelloWorld();
DLLIMPORT const char* helloJna();

#ifdef __cplusplus
}
#endif

#endif

dllmain.c文件如下。

/* Replace "dll.h" with the name of your header */
#include "dll.h"
#include <windows.h>

DLLIMPORT void HelloWorld()
{
	MessageBox(0,"Hello World from DLL!\n","Hi",MB_ICONINFORMATION);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
	switch(fdwReason)
	{
		case DLL_PROCESS_ATTACH:
		{
			break;
		}
		case DLL_PROCESS_DETACH:
		{
			break;
		}
		case DLL_THREAD_ATTACH:
		{
			break;
		}
		case DLL_THREAD_DETACH:
		{
			break;
		}
	}
	
	/* Return TRUE on success, FALSE on failure */
	return TRUE;
}

DLLIMPORT const char* helloJna()
{
    return "Hello Jna-20240105";
}

与JNI不同的是,JNA方式中不用自己写C语言代码,只需要知道C的调用接口就行了,以上面给出的代码为例,接口为HelloWorldhelloJna。注意JNA调用性能会有损耗,性能大致为JNI的1/10。

步骤2:编译成DLL文件,移动DLL文件

  1. 将c编译dll的过程略

  2. 将DLL文件移动到调用方java文件所在的同级目录,本案例编译的dll名称为:jna_c.dll

步骤3:编写java代码

import com.sun.jna.Library;
import com.sun.jna.Native;
public class JNA {
    public interface SampleLibrary extends Library {
        SampleLibrary INSTANCE = Native.load("jna_c", SampleLibrary.class);
        void HelloWorld();
        String helloJna();
    }

    public static void main(String[] args) {
      
        // INSTANCE为SampleLibrary接口的实例,通过Native.load进行初始化,使用INSTANCE.方法名就可以调用本地代码(c程序)
        SampleLibrary.INSTANCE.HelloWorld(); 
     
        String result = SampleLibrary.INSTANCE.helloJna();
        System.out.println(result);
    }

}

注意:LibraryNative都是com.sun.jna包下的,所以需要下载对应的jna包。

步骤4:编译成class

  1. 将jna包移动到java文件同级目录。

  2. javac -classpath jna-5.13.0.jar JNA.java
    

步骤5:运行

  1. 此时目录下需包含以下文件

    • jna-5.13.0.jar
    • JNA$SampleLibrary.class
    • JNA.class
    • jna_c.dll
  2. java -cp .;jna-5.13.0.jar JNA
    
    • 这个命令会将当前目录 (.) 和 jna-5.13.0.jar 都添加到类路径上,并运行 JNA 类。如果你在 Linux 或 macOS 上运行,使用分号 ; 的地方应该改成冒号 :

# Java # JNI # JNA