Java-JNI本地调用实例

209 阅读2分钟

背景

JNI 全称是Java Native Interface ,用来实现java对c/c++等编译方法的调用。常见如Jdk Object Thread 等类中也有许多基于jni的Native方法,还有一些与硬件、操作系统直接交互或者为了提高执行性能而使用jni调用。

说明

本期介绍一个使用jni示例,用java调用c++生成的链接库,实际执行c++实现的方法。大致步骤如下:

  1. 定义java native方法;
  2. 编译java得到class字节码;
  3. 生成jni链接使用的c/c++头文件;
  4. 编写c/c++代码实现;
  5. 生成链接库;
  6. 配置java执行链接库地址;
  7. 执行jni实现示例;

代码

生成java代码和编译

注意这里笔者本地根目录是/export/code/java,且这里额外创建了package

#cd /export/code/java
#mkdir -p com/test

编写Java代码

//NativeTest.java
package com.test;
public class NativeTest {
    //定义方法
    public final native void sayHello();
    //注意名字对应结果库 libxxx.so,否则找不对
    static {
        System.loadLibrary("echoHello");
     
    }
    //调用结果
    public static void main(String[] args){
        NativeTest test = new NativeTest();
        test.sayHello();
    }
}

注意loadLibrary加载的名称要对应libxxx.so的xxx部分

生成class字节码

#javac com/test/NativeTest.java

javah 生成头文件

当前目录在/export/code/java

#javah com.test.NativeTest

生成头文件如下, com_test_NativeTest.h

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

#ifndef _Included_com_test_NativeTest
#define _Included_com_test_NativeTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_test_NativeTest
 * Method:    sayHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_test_NativeTest_sayHello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

编写c++代码

这部分用来编写c++代码,具体实现我们在java中定义的native方法。当前目录在/export/code/java

//EchoHello.cpp
#include "jni.h"
#include "com_test_NativeTest.h"
#include <stdio.h>

JNIEXPORT void JNICALL Java_com_test_NativeTest_sayHello(JNIEnv *env, jobject obj){
 //执行的结果
 printf("hello , my hello !\n");
 return;
}


int main(){
 return 0;
}

编译c++代码,生成链接库

使用命令将cpp编译,生成链接库so文件。

#g++ -fPIC  -I /export/usr/jdk/jdk1.8.0_201/include/ -I /export/usr/jdk/jdk1.8.0_201/include/linux/ -shared EchoHello.cpp  -o libechoHello.so

注意:1. 需要根据环境配置使用 -I 参数引入对应的依赖,比如笔者本地ubuntu 使用 /include/linux 。 2. 注意这里链接库的命名。

关于PIC参数解释:fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。[2]

##运行结果

将当前目录添加到链接库地址

这是必须的一步,将本地生成的链接库文件添加到java执行环境中才能使用。(以下命令运行在*nix环境)

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/export/code/java

###结果展示

my@localhost:/export/code/java$ java com.test.NativeTest 
hello , my hello !
my@localhost:/export/code/java$ 

##参考链接 可能由于未知因素影响,部分链接会失效

  1. Jni的实现
  2. gcc编译参数-fPIC的一些问题