Linux系统下JNI编程示例

  • Post author:
  • Post category:linux


JNI是Java Native Interface的缩写,是Java平台的本地调用,从Java1.1就成为了Java标准的一部分,它允许Java代码和其它语言的代码进行互相调用,只要调用约定支持即可,尤其和C/C++的互相调用。

虽然使用Java与本地编译的代码进行交互,会丧失平台的可移植性,但是在特定情况下,这些问题是可以接受的,如:

1.使用一些旧的库

2.需要操作系统交互

3.提高程序的性能

一、jni介绍

Java是通过定义native方法,然后用其它语言实现该方法,最后在Java运行时,动态地加载该方法实现,通过调用native的方法,进而实现Java的本地调用。

1.实现架构

JVM封装了各种操作系统的差异性,提供了jni技术,使得开发中可以通过Java程序调用到操作系统的函数,进而与其它技术进行交互。下图是Linux平台jni的调用流程。Java应用程序通过jni接口调用动态链接库*.so,来实现jni的功能。

在这里插入图片描述

2.类型映射

Java基本数据类型与C语言基本数据类型的对应。

在这里插入图片描述

3.常用方法简介

(1)GetStringUTFLength 以字节为单位返回字符串的UTF-8长度

// jsize (JNICALL *GetStringUTFLength)(JNIEnv *env, jstring str)
int len = (*env)->GetStringUTFLength(env, str);

(2) GetStringUTFChars 返回指向字符串的UTF-8字符数组的指针。该数组在被ReleaseStringUTFChars()释放前将一直有效

// const char* (JNICALL *GetStringUTFChars)(JNIEnv *env, jstring str, jboolean *isCopy)
const char *buf = (*env)->GetStringUTFChars(env, str, NULL);

当isCopy 为JNI_FALSE,不要修改返回值,不然将改变java.lang.String的不可变语义。 一般会把isCopy设为NULL,不关心Java VM对返回的指针是否直接指向java.lang.String的内容

(3) ReleaseStringUTFChars 通知虚拟机平台相关代码无需再访问utf,utf参数是一个指针,可利用GetStringUTFChars()获得

// void (JNICALL *ReleaseStringUTFChars)(JNIEnv *env, jstring str, const char* chars)
(*env)->ReleaseStringUTFChars(env, str, buf);

(4)NewStringUTF 利用UTF-8字符数组构造新java.lang.String对象

// jstring (JNICALL *NewStringUTF)(JNIEnv *env, const char *utf)
(*env)->NewStringUTF(env, "hello");

二、jni实现步骤

我们知道java是可以调用C/C++程序的,也就时JNI编程,我们以一个最简单的Helleworld!程序,下面的程序实在Ubuntu11.04上面实现的。

(1)首先,定义java类,在java类中声明native方法,如下:

public class Main {
    
    static {
        System.loadLibrary("main");
    }

    private native void jniTest(String str);
    
    /**
     * @param args
     */
    public static void main(String[] args) {
        Main main = new Main();
        String str = "hello world!";
        main.jniTest(str);
    }

}

代码中有System.loadLibrary(“main”);也就是加载共享库,这个库是什么来头后面会说到。

该Main.cpp函数的路径放在 “/home/project/jniTest/src”

(2)通过javac编译java源文件,得到对应的class文件,这个大家都知道的。

javac Main.java

(3)通过javah生成头文件

javah Main

头文件的内容如下所示:

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

#ifndef _Included_Main
#define _Included_Main
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Main
 * Method:    jniTest
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_Main_jniTest
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

(4)根据头文件编写对应的.c文件。

首先,先生成一个同名的.c文件。

vim Main.c

再去修改其内容。

#include "Main.h"
#include <stdio.h>

JNIEXPORT void JNICALL Java_Main_jniTest(JNIEnv *evn, jobject obj, jstring jstr)
{
    const jbyte* str =(const jbyte*) (*evn)->GetStringUTFChars(evn, jstr, JNI_FALSE);
    printf("%s\n", str);
    (*evn)->ReleaseStringUTFChars(evn, jstr, (const char* )str);
    return;
}

(5)根据.c文件编译生成动态共享库,即.so文件。在这儿生成共享库时使用GCC, 必须通知编译器在何处查找此 Java 本地方法的支持文件(支持文件在不同的系统路徑有所不同),并且显式通知编译器生成位置无关的代码,如下所示。

gcc -I/usr/java/jdk1.8.0_121/include/ -I/usr/java/jdk1.8.0_121/include/linux -fPIC -shared -o libmain.so Main.c

(6)生成的动态共享库还需要告诉动态链接程序此共享文件的路径,也就时系统环境变量:LD_LIBRARY_PATH,一般可以这样设定

export LD_LIBRARY_PATH=`/home/project/jniTest/src/libmain.so`:$LD_LIBRARY_PATH  #‘地址’通过pwd查得

(7)在命令行执行Main.class就行了

[root@instance-4trd9j2v src]# java Main
hello world!

参考链接:


链接1



链接2



版权声明:本文为weixin_42425256原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。