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

9 篇文章 0 订阅
3 篇文章 0 订阅

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文件的路径"

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用 JNI 调用 Windows API 的示例代码: 首先,我们需要编写一个 C 文件,实现我们需要调用的 Windows API 函数,并将其编译成 DLL。例如,我们需要调用 MessageBoxA 函数,可以编写以下代码: ``` #include <windows.h> #include <stdio.h> #include "jni.h" JNIEXPORT void JNICALL Java_com_example_MessageBox_messageBox(JNIEnv* env, jobject obj, jstring message) { const char* utfMessage = (*env)->GetStringUTFChars(env, message, NULL); MessageBoxA(NULL, utfMessage, "Message", MB_OK | MB_ICONINFORMATION); (*env)->ReleaseStringUTFChars(env, message, utfMessage); } ``` 这个函数接受一个字符串参数,调用 Windows API 的 MessageBoxA 函数,并显示消息框。 接下来,我们需要将这个 C 文件编译成 DLL。这里使用 MinGW-w64 编译器,可以使用以下命令: ``` x86_64-w64-mingw32-gcc -shared -o MessageBox.dll MessageBox.c ``` 这将生成一个名为 MessageBox.dll 的 DLL 文件。 现在,我们可以在 Java使用 JNI 调用这个 DLL。首先,我们需要加载这个 DLL: ``` System.loadLibrary("MessageBox"); ``` 然后,我们可以调用这个 DLL 中的函数: ``` public class MessageBox { static { System.loadLibrary("MessageBox"); } public static native void messageBox(String message); public static void main(String[] args) { MessageBox.messageBox("Hello from JNI!"); } } ``` 这个 Java 类中包含一个名为 messageBox 的本地方法,它调用了我们刚刚编写的 C 函数。在 main 方法中,我们调用这个本地方法,传入一个字符串参数,这个字符串将被显示在消息框中。 这样,我们就完成了使用 JNI 调用 Windows API 的示例。需要注意的是,这个示例中使用了 MinGW-w64 编译器,如果使用其他编译器,可能需要进行相应的调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值