JNI对象传参与返回对象结果-C结构体参数【二】

JNI对象传参与返回对象结果-C结构体参数【二】,第1张

JNI对象传参与返回对象结果-C结构体参数【二】 一、背景

上一篇谈到如何创建JNI项目以及提供调用外部SO的思路,通过简单的例子实现调用C程序。
本篇继续在之前的话题做深入的探讨,主题就是对象传参。对应C语言就是结构体参数。


二、需求

做个一个简单的需求,然后实现并验证。
希望对象传参,同时能返回对象参数。
比如现在JAVA端有参数对象 Param 和结果接收对象参数ReturnParam

public class Param {
    private int a;
    private int b;
    //描述
    private String dsc;

    public Param(int a, int b, String dsc) {
        this.a = a;
        this.b = b;
        this.dsc = dsc;
    }
    // =============================================================
	// 为了节约篇幅,省掉 get set 方法  和 tostring 方法,搭建时自行补上
}
public class ReturnParam {
	// 消息
    private String msg;
    // 结果值存放
    private int c;
    // =============================================================
	// 为了节约篇幅,省掉 get set 方法  和 tostring 方法,搭建时自行补上
}

三、JAVA端

java端程序入口 HelloJNI8

public class HelloJNI8 {

    static {
        // 加载动态链接库
        System.load("/program/jni_struct/libHelloJNI.so");
    }

    
    public static native ReturnParam hello(Param param);

    public static void main(String[] args) {

        HelloJNI8 helloJNI = new HelloJNI8();
        // 调用 add方法,完成计算
        Param param = new Param(6, 8, "add");
        System.out.println("param=" + param.toString());
        // 调用 hello方法
        ReturnParam hello = HelloJNI8.hello(param);
        System.out.println("add msg(hello.msg)=" + hello.getMsg());
        System.out.println("add result(hello.c)=" + hello.getC());
        return;
    }
}

说明:
这里的目的就是为了计算 6+8的值,然后顺便传个字符串 “add” 用于验证接收。
hello方法执行后返回结果存放在 ReturnParam 中,里面包括 c 参数用于存放a+b的结果,msg存放从c程序带来的字串。
好,接下来实现C程序的处理。


四、C程序端

现有C文件HelloJNI.c

#include "jni.h"
#include "HelloJNI8.h"


const char *getStrParam(JNIEnv *env, jclass paramInClass, jobject paramIn, char *param) {
    // 获取字段id
    jfieldID fieldID = (*env)->GetFieldID(env, paramInClass, param, "Ljava/lang/String;");
    // 获取对象的字段
    jstring fieldValue = (jstring)(*env)->GetObjectField(env, paramIn, fieldID);
    // 将jstring转char *
    const char *charValue = (*env)->GetStringUTFChars(env, fieldValue, 0);
    printf("==========param %s=%sn", param, charValue);
    return charValue;
}


int getIntParam(JNIEnv *env, jclass paramInClass, jobject paramIn, char *param) {
    // 获取字段id
    jfieldID fieldID = (*env)->GetFieldID(env, paramInClass, param, "I");
    // 获取对象的字段值
    jint fieldValue = (int)(*env)->GetIntField(env, paramIn, fieldID);
    // 将jint转int
    int value = (int)fieldValue;
    printf("==========param %s=%dn", param, value);
    return value;
}


JNIEXPORT jobject JNICALL Java_HelloJNI8_hello (JNIEnv *env, jclass jc, jobject paramIn){

    printf("==========Java_HelloJNI8_hello startn");

    // 获取参数类
    jclass paramInClass = (*env)->GetObjectClass(env, paramIn);
    // 字符串参数提取  msg对应对象字段
    const char *dsc = getStrParam(env, paramInClass, paramIn, "dsc");
    // int参数提取  a,b对应对象字段
    int a = getIntParam(env, paramInClass, paramIn, "a");
    int b = getIntParam(env, paramInClass, paramIn, "b");

    // 定义返回参数 ReturnParam
    jclass cls = (*env)->FindClass(env, "ReturnParam");
    jmethodID methodID = (*env)->GetMethodID(env, cls, "", "()V");
    // 生成返回结果对象
    jobject returnResult = (*env)->NewObjectA(env, cls, methodID, 0);

    // 获取参数字段id,不同类型用不同方式
    jfieldID resultInt = (*env)->GetFieldID(env, cls, "c", "I");
    jfieldID resultStr = (*env)->GetFieldID(env, cls, "msg", "Ljava/lang/String;");

    // 填充返回结果参数
    // 这里是将 a + b 赋值给 ReturnParam的 c; 将 ReturnParam的的msg填充指定字串
    (*env)->SetIntField(env, returnResult, resultInt, a + b);
    (*env)->SetObjectField(env, returnResult, resultStr, (*env)->NewStringUTF(env, "call hello method succeeded!!!"));

    return returnResult;

}

说明:
其实最复杂的就是C这边的编码,因为没有对象的概念,所有的东西都要自己生成,其中关键的点有
①所有jni参数的处理都离不开 JNIEnv *env
②所有字段取值前都需要取得对应jfieldID ,jfieldID 需要明确数据类型,需要符号对应如下

③取字段id用GetFieldID,取值用GetIntField,GetObjectField
④返回填充如果是字符串需要 (*env)->NewStringUTF(env, “call hello method succeeded!!!”) 转化成jstring


五、运行程序

部署与搭建详情请回看 上一篇

本次文件目录结构:

shell命令合集

javac -encoding utf-8  HelloJNI8.java;
javah -jni HelloJNI8;
gcc -fPIC -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -shared -o libHelloJNI.so HelloJNI.c;
java HelloJNI8

也可以将HelloJNI8打包成 HelloJNI8.jar,然后 java -jar HelloJNI8.jar来执行


六、总结

JVAA–>C程序的动态库so,参数示意图


重点是采用对象传参,jni用对象参数接收。如果放到上一篇的外部环境SO的场景下,我们就可以变成

因为标准C和普通C都是C语言,内部不存在转换,都是C语言编写,直接用就行。

– – –

.上 一 篇.:JNI入门与进阶,JNI调用外部非标准程序SO【一】
JNA方式:JNI便捷开发框架JNA框架之入门(一)

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5432464.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-11
下一篇 2022-12-11

发表评论

登录后才能评论

评论列表(0条)

保存