1. 程式人生 > 其它 >java 封裝jni 資料返回 結構體傳遞 等

java 封裝jni 資料返回 結構體傳遞 等

近期需要c和java進行資料互動,使用jni技術,網上教程也參考不少,我這裡參考一些案例 做一些彙總,幫後來人少一些彎路

win

1 直接使用vs建立dll工程,執行相關程式碼會出現找不到jni.h的問題, 這個也好做 在專案屬性介面 vc 目錄中 包含目錄 新增java的include就可以了( win這個樣子沒問題)

另一個問題是預編譯頭問題,(此處不使用預編譯頭,因為要做到程式碼在linux也照樣跑起來)

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 不加將導致程式不識別(網上說了很多,但是沒幾個提到這一點的)

3 執行java程式時, java -Djava.library.path=. SendSMS.java

相關程式碼如下

win vs2019 工程

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

linux

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

//