[Java]使用 JNI 实现 Java 与 C 之间复杂对象/结构体的传递

JNI实战指南
本文详细介绍了如何使用Java Native Interface (JNI)实现Java与C语言之间的交互。从声明native方法到使用C语言实现,再到生成和加载动态链接库,一步步引导读者完成整个过程。

JNI是什么?

JNI是Java Native Interface的缩写,通过使用 Java 本地接口书写程序,允许Java代码和其他语言写的代码进行交互。

怎样使用?

1、声明 native 方法或属性,native方法或者属性对应的就是预留给 C 语言实现的接口。

public class JNIUtils {
    static{
        try{
            //装载动态链接库文件
            System.loadLibrary("GetMiNISeedRecord");
        }catch(UnsatisfiedLinkError e){
            System.err.println("Native code library failed to load." + e);
            System.exit(1);
        }
    }
    public native MiNiseed getMiNiSeedRecord(String inputFile);
}
public class MiNiseed {
    private String startTime;
    private ArrayList<String> simples;
    .....
}

 

2、编译声明的 Java 文件。

javac -d . MiNiseed.java
javac -d . JNIUtils.java

javah readminiseed.JNIUtils

注:①javac命令:因为在源码中使用了package的命令,所以编译的时候需要用"-d ."参数,其中"."表示在当前目录生成与上一级文件夹同名的文件夹来存放编译生成 .class文件。其次,如果声明的方法是泛型方法,如本例子所示,则指定的方法实体类同样需要编译。②javah命令:类文件名前面加上包名,生成 readminiseed_JNIUtils.h 的文件(包名_类型.h 的文件)。

3、C语言来实现函数 Java 里定义的 native 方法,和 readminiseed_JNIUtils.h文件中的接口名称保持一致。

在该包下新建一个 C 语言源文件,该文件就是接口的具体实现方法,文件的名称就是后面生成并加载的动态链接库的名称。打开上一步生成的头文件,复制头文件里预留的接口方法。

头文件样例
头文件
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "readminiseed_JNIUtils.h"

JNIEXPORT jobject JNICALL Java_readminiseed_JNIUtils_getMiNiSeedRecord(JNIEnv *env, jobject object, jstring string){
	/**
	*  1. (*env)->FindClass得到类 
	*  2. (*env)->AllocObject新建一个类的示例 
    *  3. (*env)->GetFieldID得到域ID 
    *  4. 设置域
	*
	*
	**/

	//获得需要传递的指定类
	jclass jcRec = (*env)->FindClass(env, "readminiseed/MiNiseed");

	//获取类型签名
	jfieldID jftime = (*env)->GetFieldID(env, jcRec, "startTime","Ljava/lang/String;");
    jfieldID jfsimples = (*env)->GetFieldID(env, jcRec, "simples", "Ljava/util/ArrayList;");


    // 新建一个MiNiseed对象
	jobject joRec = (*env)->AllocObject(env, jcRec);

	//对time属性赋值
    jstring jstrn = (*env)->NewStringUTF(env, "2013-01-01T12:23:34.235");
    (*env)->SetObjectField(env, joRec, jftime, jstrn);


    //初始化arraylist

    jclass list_cls = (*env)->FindClass(env,"Ljava/util/ArrayList;");
    //取得java的arraylist 初始化方法
    jmethodID arraylist_init=(*env)->GetMethodID(env,list_cls, "<init>","()V");
    //取得 add 方法
    jmethodID arraylist_add= (*env)->GetMethodID(env,list_cls,"add","(Ljava/lang/Object;)Z");
    //新建arraylist对象
    jobject obj_arraylist =(*env)->NewObject(env,list_cls, arraylist_init, "");
    
    for(int i = 0 ; i < 3 ; i++)  
    {  
        jstring str = (*env)->NewStringUTF(env,"Native");  
        //执行Arraylist类实例的add方法,添加一个属性 
        (*env)->CallBooleanMethod(env,obj_arraylist,arraylist_add, str);    
    } 
    //将对象中的arraylist类型的属性赋值 
    (*env)->SetObjectField(env, joRec, jfsimples, obj_arraylist);

    return joRec;

}

 

4、编译,生成动态库文件。

gcc  -fPIC -D_REENTRANT -I/home/ubuntu/services/java/jdk8/include -I/home/ubuntu/services/java/jdk8/include/linux -c GetMiNISeedRecord.c

注:-I 后面接的是java安装目录中 jni.h 文件所在目录、以及 jni_md.h 所在目录。

gcc -shared GetMiNISeedRecord.o -o libGetMiNISeedRecord.so

注:.so 文件命名规范:遵循 lib + 文件名 + 扩展名的原则,否则,加载动态链接库的时候找不到文件。

5、使用生成的动态库文件。(在第一步中已经完成,写在静态方法中,只要初始化该类就会首先加载库)

System.loadLibrary("GetMiNISeedRecord");

注:库的名称就是 .so文件的名称(去掉lib)或者 C 语言源文件的名称。

6、直接运行可能会报错 java.lang.UnsatisfiedLinkError,需要在 idea 中设置 run->Edit Configurations-> VM Options 添加一行内容。

-Djava.library.path=".so文件的路径"

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值