Java JNI实现原理解析

前言

本篇文章主要从几个方面来阐述下java jni的原理机制。1. 什么是java jni? 2. java jni有什么作用? 3. java jni应用场景有哪些?4. java jni实现原理5. java jni如何使用;6. 使用实例;下面我们逐一进行解析。

 

什么是java jni

JNI(Java Native Interface)是JAVA标准平台中的一个重要功能,它弥补了JAVA的与平台无关这一重大优点的不足,在JAVA实现跨平台的同时,也能与其它语言(如C、C++)的动态库进行交互,给其它语言发挥优势的机会。有了JAVA标准平台的支持,使JNI模式更加易于实现和使用。

系统环境代指本地操作系统环境,它有自己的本地库和CPU指令集。本地程序(Native Applications)使用C/C++这样的本地语言来编写,被编译成只能在本地系统环境下运行的二进制代码,并和本地库链接在一起。本地程序和本地库一般地会依赖于一个特定的本地系统环境。比如,一个系统下编译出来的C程序不能在另一个系统中运行。

JNI的强大特性使我们在使用JAVA平台的同时,还可以重用原来的本地代码。作为虚拟机实现的一部分,JNI允许JAVA和本地代码间的双向交互。

java jni有什么作用

上一部分已经大概说了java jni的作用,JNI是java平台的一部分,通过java jni可以实现与其他底层语言(例如C、C++)进行交互。JNI是完善JAVA功能的一个重要功能,一方面JVM封装了各种操作系统的差异性,使用JAVA程序可以跨平台运行,另一方面JNI提供了java程序与操作系统相关功能函数交互的接口,不失java功能的全面性。

 

java jni应用场景

1. 解决性能问题

在程序对时间敏感或对性能要求特别高时,有必要使用更底层的语言,例如使用C或者C++来实现具体功能,然后在java中直接调用这些功能。

2.  解决本机平台接口调用问题

JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能。解决JAVA对本地操作的一种方法就是JNI。JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式)。通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法。

 3. 嵌入式开发应用

“一次编程,到处使用”的Java软件概念原本就是针对网上嵌入式小设备提出的,几经周折,目前SUN公司已推出了J2ME(Java 2 P1atform Micro Edition)针对信息家电的Java版本,其技术日趋成熟,开始投入使用。SUN公司Java虚拟机(JVM)技术的有序开放,使得Java软件真正实现跨平台运行,即Java应用小程序能够在带有JVM的任何硬软件系统上执行。加上Java语言本身所具有的安全性、可靠性和可移植性等特点,对实现瘦身上网的信息家电等网络设备十分有利,同时对嵌入式设备特别是上网设备软件编程技术产生了很大的影响。也正是由于JNI解决了本机平台接口调用问题,于是JNI在嵌入式开发领域也是如火如荼。

 

 java jni实现原理

JNI最重要的设计目标就是在不同操作系统上的JVM之间提供二进制兼容,做到一个本地库不需要重新编译就可以运行不同的系统的JVM上面。

调用过程示意图如下:

从代理模式理解JNI:

 

 java jni如何使用

1. 在java类中申明Native方法(此类为JNI类程序,担任代理角色),并编译成class文件(在此类中同时也需要加载需要使用的类库);

2. 用javah将第一步的class文件生成头文件(具体命令:javah -jni XXX,  javah程序统一了java中的native方法、头文件中的函数名和动态库中的函数实现之间的对应关系)

3. 用其他语言(C、C++等)实现上述头文件中的函数,生成动态库,供java程序使用;

4. 发布java和动态库。

 

使用实例

使用JAVA程序调用C函数进行简单加法计算。这个过程包含下面几个步骤:

  1. 创建一个类(AddTest.java)声明本地方法。

  2. 使用javac编译源文件AddTest.java,产生AddTest.class。使用javah –jni来生成C头文件(test_AddTest.h),这个头文件里面包含了本地方法的函数原型。

  3. 用C代码写函数原型的实现。

  4. 把C函数实现编译成一个本地库,创建libcommon.dll或者libcommon.so(自定义本地库名称)。

  5. 使用java命令运行AddTest程序,类文件AddTest.class和本地库libcommon.dll或者libcommon.so)在运行时被加载。

 

1.创建一个Java类,里面包含着一个native的方法和加载库的方法loadLibrary。AddTest.java代码如下:

package test;
public class AddTest{
        static{
                System.loadLibrary("common");
        }

        public native int nativeAdd(int x,int y);

        public static void main(String[] args){
                AddTest add = new AddTest();
                System.out.printf("%d\n",add.nativeAdd(123,123));
        }
}

代码很简单,这里声明了nativeAdd(int x,inty)的方法,执行的时候简单的打出执行的结果。另外这里调用API加载名称叫common的库,接下来就来实现这个库。

假设当前目录结构如下:

- | - test | AddTest.java

2.生成JNI调用需要的头文件

javac test/AddTest.java
javah -jni test.AddTest

现在的目录结构如下:

- | - test 

         | AddTest.java

         | AddTest.class

| - test_AddTest.h

test_AddTest.h头文件内容如下:

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

#ifndef _Included_test_AddTest
#define _Included_test_AddTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     test_AddTest
 * Method:    nativeAdd
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_test_AddTest_nativeAdd
  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

3.native方法的实现
这里新增common.c文件来实现之前声明的native方法,目录结构如下:

- | - test

        | AddTest.java

        | AddTest.class

| - test_AddTest.h

| - common.c

common.c的内容如下:

#include "test_AddTest.h"

JNIEXPORT jint JNICALL Java_test_AddTest_nativeAdd 
(JNIEnv * env, jobject obj, jint x, jint y){
        return x+y;
}

这里的实现只是简单的把两个参数相加,然后返回。

4.生成动态链接库

gcc -shared -I /usr/lib/jdk1.6/include
-I /usr/lib/jdk1.6/include/linux common.c -o libcommon.so

注意这里几个gcc的选项,-shared是说明要生成动态库,而两个 -I的选项,是因为我们用到<jni.h>相关的头文件,放在<jdk>/include 和 <jdk>/include/linux两个目录下。
最后需要注意一点的是 -o 选项,我们在java代码中调用的是System.loadLibrary("xxx"),那么生成的动态链接库的名称就必须是libxxx.so的形式(这里指Linux环境),否则在执行java代码的时候,就会报 java.lang.UnsatisfiedLinkError: no XXX in java.library.path 的错误!也就是说找不到这个库,我在这里被坑了一小段时间。
好了,现在的目录结构如下:

- | - test

        | AddTest.java

        | AddTest.class

| - test_AddTest.h

| - common.c

| - libcommon.so

5.执行代码验证结果

java -Djava.library.path=. test.AddTest
246

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值