java 封装jni 数据返回 结构体传递 等

近期需要c和java进行数据交互,使用jni技术,网上教程也参考不少,我这里参考一些案例 做一些汇总,帮后来人少一些弯路

win

1 直接使用vs创建dll工程,运行相关代码会出现找不到jni.h的问题, 这个也好做 在项目属性界面 vc 目录中 包含目录 添加java的include就可以了( win这个样子没问题)

java 封装jni 数据返回 结构体传递 等

 

 

 另一个问题是预编译头问题,(此处不使用预编译头,因为要做到代码在linux也照样跑起来)

java 封装jni 数据返回 结构体传递 等

 

 

2 编译时候切换debug和release,可能需要重新填写以上信息 另一点,debug生成的 dll 库,可能导致在无vc环境的电脑无法使用 因此win对外发布时,需要使用release生成的dll库

3 关于动态库编译时,头文件生成 建议使用java自己生成.如下

对于java10 之前的 使用 javac SendSMS.java生成.class文件再 javach SendSMS生成.h文件

对于java10 之后的 使用 javac -h . SendSMS.java

class SendSMS {
	public native int SmsInit();
	public native int SmsSend(byte[] mobileNo, byte[] smContent);
	public native int foo(Foo fooObj);
	public native int SmsRead(int  x,int y,double[]  cText);
}

自动生成的.h文件如下  SendSMS.h

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

#ifndef _Included_SendSMS
#define _Included_SendSMS
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     SendSMS
 * Method:    SmsInit
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_SendSMS_SmsInit
  (JNIEnv *, jobject);

/*
 * Class:     SendSMS
 * Method:    SmsSend
 * Signature: ([B[B)I
 */
JNIEXPORT jint JNICALL Java_SendSMS_SmsSend
  (JNIEnv *, jobject, jbyteArray, jbyteArray);

/*
 * Class:     SendSMS
 * Method:    foo
 * Signature: (LFoo;)I
 */
JNIEXPORT jint JNICALL Java_SendSMS_foo
  (JNIEnv *, jobject, jobject);

/*
 * Class:     SendSMS
 * Method:    SmsRead
 * Signature: (II[D)I
 */
JNIEXPORT jint JNICALL Java_SendSMS_SmsRead
  (JNIEnv *, jobject, jint, jint, jdoubleArray);

#ifdef __cplusplus
}
#endif
#endif

后面就根据生成的这个.h开发响应的函数 如下  SendSMS.c

#include <jni.h>
typedef struct chuanStruts {
    int y;
    double doubletext[40];
} smsstruts;

typedef struct Foo {
    int len;
    char name[100];
} Foo_t;

JNIEXPORT jint JNICALL Java_SendSMS_SmsInit(JNIEnv *ev, jobject obj) {
    return SmsInit();
    //调用sms.c里的SmsInit方法
}
JNIEXPORT jint JNICALL Java_SendSMS_SmsSend(JNIEnv *ev, jobject obj, jbyteArray mobileno, jbyteArray smscontent) {
    char * psmscontent ;
    //jsize thearraylengthj = (*env)->getarraylength(env,mobileno);
    jbyte * arraybody = (*ev)->GetByteArrayElements(ev,mobileno,0);
    char * pmobileno = (char *)arraybody;
    printf("[%s]/n ", pmobileno);
    //jsize size = (*env)->getarraylength(env,smscontent);
    arraybody = (*ev)->GetByteArrayElements(ev,smscontent,0);
    psmscontent = (char *)arraybody;
    return SmsSend(pmobileno,psmscontent);
    //调用sms.c里的SmsSend方法
}
JNIEXPORT jint JNICALL Java_SendSMS_SmsRead (JNIEnv *ev, jobject obj, jint x,jint y, jdoubleArray doubletext) {
    smsstruts example;
    //自己构建的example结构体变量
    double * psmscontent ;
    int i;
    jdouble * arraybody = (*ev)->GetDoubleArrayElements(ev,doubletext,0);
    psmscontent = (double *)arraybody;
    printf("%f",*psmscontent);
    printf("%f",*(psmscontent+1));
    example.y= y;
    for (i=0;i<2;i++) {
        example.doubletext[i] = *(psmscontent+i);
    }
    return SmsRead(x,&example);
    //调用sms.c里的SmsRead方法
}

JNIEXPORT jint JNICALL Java_SendSMS_foo(JNIEnv* env, jobject obj, jobject fooObj) {

    //printf("debug -10\n");
    char buf[1024];
    printf("debug -9\n");
    memset(buf, 0x00, 1024);
    printf("debug -8\n");
    Foo_t* bar = (Foo_t*)buf;//malloc(sizeof(Foo_t));

    printf("debug -7\n");
    jclass clazz;
    printf("debug -6\n");
    jfieldID fid;
    printf("debug -5\n");

    //init the bar data of C
    char* t = "Yachun Miao";
    printf("Java_SendSMS_foo: %s %d\n", t, strlen(t));
    strcpy(bar->name, "Yachun Miao");
    printf("debug -4\n");
    bar->len = strlen(bar->name);

    printf("Java_SendSMS_foo: %s %d\n", bar->name, bar->len);

    printf("debug -3\n");
    // mapping bar of C to foo
    clazz = (*env)->GetObjectClass(env, fooObj);
    printf("debug -2\n");
    if (0 == clazz) {
        printf("debug -1\n");
        printf("GetObjectClass returned 0\n");
        return (-1);
    }
    printf("debug 0\n");
    fid = (*env)->GetFieldID(env, clazz, "len", "I");
    printf("debug 1\n");
    (*env)->SetLongField(env, fooObj, fid, bar->len);

    printf("debug 2\n");

    fid = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;");
    printf("debug 3\n");
    jstring name = (*env)->NewStringUTF(env, bar->name);
    printf("debug 4\n");
    (*env)->SetObjectField(env, fooObj, fid, name);
    printf("debug 5\n");


    //free(bar);
    return 0;
}

开发时,其他文件如下 sms.h

#ifndef _TX_SMS_H_
#define _TX_SMS_H_
#ifdef __cplusplu*
extern "C" {
    #endif
    typedef struct tagSmsEntry {
        int index;
        double text[40];
    } SmsEntry;
    int SmsInit(void);
    //无参数
    int SmsSend(char *phonenum, char *content);
    //指针变量参数
    int SmsRead(int x,SmsEntry *entry);

    //结构体参数
    #ifdef __cplusplus
}
#endif
#endif

sms.c

#include "sms.h" 
 int SmsInit(void) {
    printf("welcome \n");
    return 1;
}
int SmsSend(char *phonenum, char *content) {
    printf("liuxiao \n");
    char a[100];
    memset(a, 0x00, 100);
    memset(a, 0x31, 99);
    printf("%s\n",a);
    printf("%s %s\n",phonenum,content);
    return 2;
}
int SmsRead(int x,SmsEntry *entry) {
    int i;
    printf("mingxin\n");
    printf("%d \n",x);
    for (i=0;i<2;i++) {
        printf("%f ",entry->text[i]);
    }
    return 3;
}

注意通过经验总结发现,不能使用 malloc(sizeof(Foo_t))

使用之后,程序直接崩溃,所以只能使用临时数组,具体原因有待考究

 

4 生成动态库时点击 生成->生成解决方案 

 

linux 

1 生成库文件时,前面必须添加lib(感觉就是一xx操作)

2 安装java时,不要使用yum自带的安装,里面内容不全,没有include头文件,因此需要自己到官网下载

 具体指令如下 

mkdir usr/local/java
cd usr/local/java
wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz
tar -zxvf jdk-17_linux-x64_bin.tar.gz

然后就是修改 配置文件 追加 内容

JAVA_HOME=/usr/local/java/jdk-17.0.2
PATH=$PATH:$JAVA_HOME/bin
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export JAVA_HOME CLASSPATH PATH

最后 source /etc/profile

检验java -version

如果不对,就 which java 查看当前java 路径 再rm -rf 路径   此时,应该就可以看到版本号正确了

 

2 编译 注意 引入外部头文件时候 是大写的字母i 这个很坑,换成小写报错,只能大写,但是和小写字母L太像了注意 生成.so文件时,必须在文件前加上lib  不加将导致程序不识别(网上说了很多,但是没几个提到这一点的)

java 封装jni 数据返回 结构体传递 等

 

 

3 运行java程序时,   java -Djava.library.path=. SendSMS.java

java 封装jni 数据返回 结构体传递 等

 

 

相关代码如下

win vs2019 工程

https://files.cnblogs.com/files/RYSBlog/JCProtocol2.zip?t=1643077628

linux 

https://files.cnblogs.com/files/RYSBlog/linuxso.zip?t=1643077619

//

 

上一篇:Node.js 之多进程


下一篇:快速傅立叶变换