Java中使用JNI调用本地动态库的方法(图文详解加代码示例)

您所在的位置:网站首页 如何调用动态库的函数 Java中使用JNI调用本地动态库的方法(图文详解加代码示例)

Java中使用JNI调用本地动态库的方法(图文详解加代码示例)

#Java中使用JNI调用本地动态库的方法(图文详解加代码示例)| 来源: 网络整理| 查看: 265

详解Java中使用JNI调用本地动态库的方法 第0步:创建被调用的示例动态库第一步:创建调用动态库的Java代码第二步:编译生成调动动态库的class文件第三步:生成中介(或代理)动态库的头文件第四步:编译生成中介(或代理)动态库第五步:完善本地Java调用代码。第六步:验证执行附1:可选方法:JNative。附2:Linux平台下的调用方法。第一步、Java调用部分第二步、中介动态库代码部分第三步、编译生成class,中介动态库及运行class第四步、过程注意点1、引用和实际动态库名称的lib前缀2、 环境变量设置

日常业务开发过程中,如果要使用操作系统本身或第三方功能,就会经常遇到使用系统或第三方动态库。在Java开发平台上,调用动态库需要使用到 JNI(Java Native Interface)技术。

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

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

闲话不说,直接上示例,请各位读者根据下面的分步操作,来一起探究JNI的技术使用。 总体关系图 一直觉得缺张总体图,快22年年底了,安排上!

第0步:创建被调用的示例动态库

本例基于Windows平台,首先生成被调用的目的dll动态库:Lib4JNI.dll Lib4JNI动态库中导出了一个add方法。在本例中用JNI去调用。

// dllmain.cpp : Defines the entry point for the DLL application. #include "stdafx.h" #include "stdlib.h" #include "stdio.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } extern "C" int _declspec(dllexport) add(int a, int b) { int iResult = a + b; printf("%d + %d = ", a, b); return iResult; }

接下来要做的是,使用中介(或者叫代理)动态库去调用目的动态库(目的动态库就是本例中的Lib4JNI.dll)。

在Java中,是不可以直接调用目的动态库的。因此,需要有一个中介(或代理),由本地的Java代码先调用这个中介(或代理)动态库,再由这个中介(或代理)动态库调用目的动态库。我们给中介dll起个名字,叫做Lib2Invoke.dll。

生成这个中介dll的过程比较复杂,下面,进入第一步,来详细说明。

第一步:创建调用动态库的Java代码 public class TestJNI { public native int getNumber(int a, int b); //声明Native方法 public static void main(String[] args) { } }

将以上代码,以文本形式,保存为TestJNI.java。

第二步:编译生成调动动态库的class文件 javac TestJNI.java 第三步:生成中介(或代理)动态库的头文件

在JNI中,中介(或代理)动态库是十分重要的概念。因为Java一般不直接调用系统库或第三方库,而是通过生成这个中介dll,由JVM调用中介dll,再由中介dll去调用系统库或第三方库。

javah TestJNI`

运行后,jdk负责自动生成用于编译中介dll的头文件,本例中TestJNI.h生成的文件示例如下:

/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class TestJNI */ #ifndef _Included_TestJNI #define _Included_TestJNI #ifdef __cplusplus extern "C" { #endif /* * Class: TestJNI * Method: getNumber * Signature: (II)I */ JNIEXPORT jint JNICALL Java_TestJNI_getNumber (JNIEnv *, jobject, jint, jint); #ifdef __cplusplus } #endif #endif 第四步:编译生成中介(或代理)动态库

为什么到这里才开始生成中介dll呢?因为,我们需要上一步生成的TestJNI.h,作为中介dll的头文件。详细请参考代码。

// dllmain.cpp : Defines the entry point for the DLL application. #include "stdafx.h" #include "jni.h" //在这里,我们要注意的是,需要引用 #include "TestJNI.h" #ifdef WIN32 #ifdef _X86_ #define _T(x) x #else #ifdef _AMD64_ #define _T(x) L ## x #endif #endif #endif BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } JNIEXPORT jint JNICALL Java_TestJNI_getNumber (JNIEnv * env, jobject o, jint x, jint y) { typedef int(*ADD)(int, int);//函数指针类型 HINSTANCE Hint = ::LoadLibrary(_T("Lib4JNI.dll"));//加载我们刚才生成的dll ADD add = (ADD)GetProcAddress(Hint, "add");//取得dll导出的add方法 return add(x, y); FreeLibrary(Hint); }

编译完成后,生成了我们需要的中介(或代理)动态库Lib2Invoke.dll。

第五步:完善本地Java调用代码。 public class TestJNI { public native int getNumber(int a, int b); public static void main(String[] args) { System.loadLibrary("Lib2Invoke"); TestJNI p = new TestJNI(); System.out.println(p.getNumber(100, 100)); } }

编译完成后,把生成的class文件,和Lib4JNI.dll、Lib2Invoke.dll放在一个目录下。

第六步:验证执行 java TestJNI 200

最后一步,就是测试下是否成功。执行成功的话,即会输出结果。

附1:可选方法:JNative。

Java中,也提供了一些包用于简化对目的动态库的调用过程(覆盖上的第一到第六步)。继续以程序员特有的本质,少说话,多代码:

import org.xvolks.jnative.JNative; import org.xvolks.jnative.Type; import org.xvolks.jnative.exceptions.NativeException; public class TestJNI { static JNative myjnative = null; public int getnumber(int a, int b) throws NativeException, IllegalAccessException { try { if (myjnative == null) { myjnative = new JNative("Lib4JNI.dll", "add"); // 直接调用目的动态库和其中的方法 myjnative.setRetVal(Type.INT); } myjnative.setParameter(0, a); myjnative.setParameter(1, b); myjnative.invoke(); // 返回对目的动态库的调用结果 return myjnative.getRetValAsInt(); } finally { if (myjnative != null) { myjnative.dispose(); } } } public static void main(String[] args) throws NativeException, IllegalAccessException { test uc = new test(); int result = uc.getnumber(1,100); System.err.println("result:" + result); } }

优点是:不用中介dll,不用生成.h中介头文件 缺点是:(其实说缺点是不准确的,只是为了对齐优点)需要依赖外部包JNative支持。

附2:Linux平台下的调用方法。

基本步骤和在Windows平台上相同。下面给出主要有区别处的示例代码。

第一步、Java调用部分

NativeAgent.java

public class NativeAgent { static { try { System.loadLibrary("NativeAgent"); } catch(UnsatisfiedLinkError e) { System.err.println(">>> Can not load library: " + e.toString()); } } // 声明对Native函数toConsole(String s)的调用 public native int toConsole(String s); public static void main(String[] args) { NativeAgent na = new NativeAgent(); na.toConsole("This is a JNI Project test.\n"); } } 第二步、中介动态库代码部分

NativeAgent.h

/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class NativeAgent */ #ifndef _Included_NativeAgent #define _Included_NativeAgent #ifdef __cplusplus extern "C" { #endif /* * Class: NativeAgent * Method: toConsole * Signature: (Ljava/lang/String;)I */ JNIEXPORT jint JNICALL Java_NativeAgent_toConsole (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif

NativeAgent.c

#include #include #include #include "NativeAgent.h" JNIEXPORT jint JNICALL Java_NativeAgent_toConsole(JNIEnv *pEnv, jobject obj, jstring str) { const char* msg = (*pEnv)->GetStringUTFChars(pEnv, str, 0); printf("%s", msg); return 0; } 第三步、编译生成class,中介动态库及运行class javac NativeAgent.java javah NativeAgent gcc -o libNativeAgent.so -I/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.102-1.b14.el7_2.x86_64/include -I/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.102-1.b14.el7_2.x86_64/include/linux -I. -fPIC -shared NativeAgent.c java -Djava.library.path=. NativeAgent 第四步、过程注意点

这里有二个非常重要的地方,要十分注意:

1、引用和实际动态库名称的lib前缀

对于System.loadLibrary("NativeAgent"); 在Linux下,动态库输出的文件名要是libNativeAgent.so。也就是说,如果System.loadLibrary("XXX");那么,在导出动态库时,动态库的名字就要是libXXX。否则,会报错:

java.lang.UnsatisfiedLinkError: no NativeAgent in java.library.path Exception in thread "main" java.lang.UnsatisfiedLinkError: NativeAgent.toConsole(Ljava/lang/String;)I at NativeAgent.toConsole(Native Method) at NativeAgent.main(NativeAgent.java:20) 2、 环境变量设置

Linux一般默认的java.library.path在/usr/lib下。也可以自己通过VM参数-Djava.library.path=/usr/lib来显式的指定;或者通过增加环境变量export LD_LIBRARY_PATH=~/JavaNativeTest:$LD_LIBRARY_PATH



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3