NDK开发:C++对象与Java对象的传递

  • Post author:
  • Post category:java


最近在项目中需要android和window之间数据同步,其中window程序是C++写的,生成的一些文件是结构体文件,所以需要能够吧Java对象传给C++生成结构体文件,能够被C++程序识别;同时C++需要把结构体文件读出来传给Java来识别,这里使用了android的NDK开发。

Java层:

public class Student {
    public  String name;
    public  long id;
    public  int age;
    public  float height;
    public  float weight;
    public  double score;
    public  boolean sex;
    public ArrayList<Tpoint> tpoints;
    public Student() {
    }

    public Student(String name, long id, int age, float height, float weight, double score, boolean sex, ArrayList<Tpoint> tpoints) {
        this.name = name;
        this.id = id;
        this.age = age;
        this.height = height;
        this.weight = weight;
        this.score = score;
        this.sex = sex;
        this.tpoints = tpoints;
    }

    ...
    //省略get,set方法
    ...
}

这里的Student类里包含Tpoint对象集合(这里Tpoint可以理解为X,Y坐标)

public class Tpoint {
    public float x;
    public float y;

    public Tpoint() {
    }

    public Tpoint(float x, float y) {
        this.x = x;
        this.y = y;
    }

    ...
    //省略get,set方法
    ...
}

MainActivity主要代码段:

tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ArrayList<Tpoint> tpoints = initTpoints();
                Student student = new Student();
                student.setName("小米");
                student.setAge(10);
                student.setHeight(135.2f);
                student.setWeight(45.2f);
                student.setId(12345678);
                student.setScore(80.5);
                student.setSex(false);
                student.setTpoints(tpoints);

                byte[] data = parseObjectToByteArray(student);
                if (data != null) {
                    Log.d("jokey", "data: " + data.length);
                    Student stu = parseByteArrayToObject(data);
                    if (stu != null) {
                        Log.d("jokey", "stu: " + stu.toString());
                    }
                } else {
                    Log.d("jokey", "data: null");
                }
            }
        });
    private ArrayList<Tpoint> initTpoints() {
        ArrayList<Tpoint> tpoints = new ArrayList<>();
        tpoints.add(new Tpoint(1, 1));
        tpoints.add(new Tpoint(2, 2));
        tpoints.add(new Tpoint(3, 3));
        tpoints.add(new Tpoint(4, 4));
        return tpoints;
    }
    public native byte[] parseObjectToByteArray(Student student) ;

    public native Student parseByteArrayToObject(byte[] data) ;
parseObjectToByteArray(Student student)方法是把Student对象传给Native,返回C++中STUDENT结构体的byte数组
parseByteArrayToObject(byte[] data)方法是parseObjectToByteArray方法的反步骤

Native层:

struct TPOINT {

    float x;
    float y;
};

struct STUDENT {

    char name[50];
    long id;
    int age;
    float height;
    float weight;
    double score;
    bool sex;
    TPOINT tpoint[100];
};

这里结构体STUDENT和TOINT对应Java层的Student和Tpoint

#include <jni.h>
#include <string>
#include <iostream>
#include <android/log.h>
#include "Struct.h"//里面是STUDENT和TPOINT结构体

extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_medlander_candjavaobject_MainActivity_parseObjectToByteArray(JNIEnv *env, jobject thiz,jobject obj_stu) {

    jclass stucls = env->GetObjectClass(obj_stu); //或得Student类引用
    if (stucls == NULL) {
        __android_log_print(ANDROID_LOG_INFO, "jni_jokey", "GetObjectClass obj_stu failed");
    }
    jfieldID nameFieldID = env->GetFieldID(stucls, "name", "Ljava/lang/String;"); // 获得属性ID
    jfieldID idFieldID = env->GetFieldID(stucls, "id", "J"); //获得得Student类的属性id
    jfieldID ageFieldID = env->GetFieldID(stucls, "age", "I"); //获得得Student类的属性id
    jfieldID heightFieldID = env->GetFieldID(stucls, "height", "F"); //获得得Student类的属性id
    jfieldID weightFieldID = env->GetFieldID(stucls, "weight", "F"); //获得得Student类的属性id
    jfieldID scoreFieldID = env->GetFieldID(stucls, "score", "D"); //获得得Student类的属性id
    jfieldID sexFieldID = env->GetFieldID(stucls, "sex", "Z"); //获得得Student类的属性id

    jfieldID tpointFieldID = env->GetFieldID(stucls, "tpoints",
                                             "Ljava/util/ArrayList;"); //获得Student类的ArrayList属性id

    jobject tpoints = env->GetObjectField(obj_stu, tpointFieldID);

    env->DeleteLocalRef(stucls);

    jstring name = (jstring) env->GetObjectField(obj_stu, nameFieldID);//获得属性值
    jlong id = env->GetLongField(obj_stu, idFieldID);
    jint age = env->GetIntField(obj_stu, ageFieldID);  //获得属性值
    jfloat height = env->GetFloatField(obj_stu, heightFieldID);
    jfloat weight = env->GetFloatField(obj_stu, weightFieldID);
    jdouble score = env->GetDoubleField(obj_stu, scoreFieldID);
    jboolean sex = env->GetBooleanField(obj_stu, sexFieldID);
    const char *c_name = env->GetStringUTFChars(name, NULL);//转换成 char *

    STUDENT student;
    memset(&student, 0, sizeof(STUDENT));

    __android_log_print(ANDROID_LOG_DEBUG, "jni_jokey", "name: %s", c_name);
    __android_log_print(ANDROID_LOG_DEBUG, "jni_jokey", "id: %lli", id);
    __android_log_print(ANDROID_LOG_DEBUG, "jni_jokey", "age: %d", age);
    __android_log_print(ANDROID_LOG_DEBUG, "jni_jokey", "height: %f", height);
    __android_log_print(ANDROID_LOG_DEBUG, "jni_jokey", "weight: %f", weight);
    __android_log_print(ANDROID_LOG_DEBUG, "jni_jokey", "score: %lf", score);
    if (sex)
        __android_log_print(ANDROID_LOG_DEBUG, "jni_jokey", "sex: 男");
    else
        __android_log_print(ANDROID_LOG_DEBUG, "jni_jokey", "sex: 女");

    memcpy(student.name, c_name, sizeof(student.name));
    //释放引用
    env->ReleaseStringUTFChars(name, c_name);

    student.id = static_cast<long>(id);
    student.age = age;
    student.height = height;
    student.weight = weight;
    student.score = score;
    student.sex = sex;
    if (tpoints != NULL) {
        int i;
        jclass cls_arraylist = env->GetObjectClass(tpoints);
        jmethodID arraylist_get = env->GetMethodID(cls_arraylist, "get", "(I)Ljava/lang/Object;");
        jmethodID arraylist_size = env->GetMethodID(cls_arraylist, "size", "()I");
        jint len = env->CallIntMethod(tpoints, arraylist_size);
        __android_log_print(ANDROID_LOG_DEBUG, "jni_jokey", "arraylist_size: %d", len);
        for (i = 0; i < len; i++) {
            __android_log_print(ANDROID_LOG_DEBUG, "jni_jokey", "i: %d", i);
            jobject obj_student = env->CallObjectMethod(tpoints, arraylist_get, i);
            jclass cls_student = env->GetObjectClass(obj_student);

            jfieldID xFieldID = env->GetFieldID(cls_student, "x", "F"); // 获得属性ID
            jfieldID yFieldID = env->GetFieldID(cls_student, "y", "F"); // 获得属性ID

            jfloat x = env->GetFloatField(obj_student, xFieldID);//获得属性值
            jfloat y = env->GetFloatField(obj_student, yFieldID);//获得属性值
            __android_log_print(ANDROID_LOG_DEBUG, "jni_jokey", "x: %f", x);
            __android_log_print(ANDROID_LOG_DEBUG, "jni_jokey", "y: %f", y);
            student.tpoint[i].x = x;
            student.tpoint[i].y = y;
            env->DeleteLocalRef(obj_student);
            env->DeleteLocalRef(cls_student);
        }
        env->DeleteLocalRef(cls_arraylist);
        env->DeleteLocalRef(tpoints);
    } else {
        __android_log_print(ANDROID_LOG_INFO, "jni_jokey", "GetObjectField tpoints failed");
    }
    int length = sizeof(STUDENT);
    jbyteArray array = env->NewByteArray(length);
    env->SetByteArrayRegion(array, 0, sizeof(STUDENT), (jbyte *) &student);
    //结束释放
    env->ReleaseByteArrayElements(array, env->GetByteArrayElements(array, JNI_FALSE), 0);

    return array;
}
extern "C"
JNIEXPORT jobject JNICALL
Java_com_medlander_candjavaobject_MainActivity_parseByteArrayToObject(JNIEnv *env, jobject thiz,jbyteArray data) {
    jbyte *bBuffer = env->GetByteArrayElements(data, NULL);
    char *buf = (char *) bBuffer;
    STUDENT student;
    memcpy(&student, buf, sizeof(STUDENT));
    //释放
    env->ReleaseByteArrayElements(data, bBuffer, 0);

    //关于包描述符,这儿可以是 com/medlander/candjavaobject/Student 或者是 Lcom/medlander/candjavaobject/Student;
    //   这两种类型 都可以获得class引用
    jclass stucls = env->FindClass("com/medlander/candjavaobject/Student"); //获得Student类引用
    if (stucls == NULL) {
        __android_log_print(ANDROID_LOG_INFO, "jni_jokey", "stucls is null");
    }

    //获得得该类型的构造函数  函数名为 <init> 返回类型必须为 void 即 V
    jmethodID constrocMID = env->GetMethodID(stucls, "<init>", "()V");

    jobject stu_obj = env->NewObject(stucls, constrocMID);  //构造一个对象,调用该类的构造函数,并且传递参数

    jfieldID nameFieldID = env->GetFieldID(stucls, "name", "Ljava/lang/String;"); // 获得属性ID
    jfieldID idFieldID = env->GetFieldID(stucls, "id", "J"); //获得得Student类的属性id
    jfieldID ageFieldID = env->GetFieldID(stucls, "age", "I"); //获得得Student类的属性id
    jfieldID heightFieldID = env->GetFieldID(stucls, "height", "F"); //获得得Student类的属性id
    jfieldID weightFieldID = env->GetFieldID(stucls, "weight", "F"); //获得得Student类的属性id
    jfieldID scoreFieldID = env->GetFieldID(stucls, "score", "D"); //获得得Student类的属性id
    jfieldID sexFieldID = env->GetFieldID(stucls, "sex", "Z"); //获得得Student类的属性id
    jfieldID tpointFieldID = env->GetFieldID(stucls, "tpoints",
                                             "Ljava/util/ArrayList;"); //获得Student类的ArrayList属性id

//    jobject tpoints = env->GetObjectField(stu_ojb, tpointFieldID);

    jstring name = env->NewStringUTF(student.name);
    env->SetObjectField(stu_obj, nameFieldID, name);
    env->SetLongField(stu_obj, idFieldID, student.id);
    env->SetIntField(stu_obj, ageFieldID, student.age);
    env->SetFloatField(stu_obj, heightFieldID, student.height);
    env->SetFloatField(stu_obj, weightFieldID, student.weight);
    env->SetDoubleField(stu_obj, scoreFieldID, student.score);
    env->SetBooleanField(stu_obj, sexFieldID, static_cast<jboolean>(student.sex));

    env->DeleteLocalRef(name);

    jclass list_cls = env->FindClass("java/util/ArrayList");

    if (list_cls == NULL) {
        __android_log_print(ANDROID_LOG_INFO, "jni_jokey", "list_cls is null");
    }
    jmethodID list_costruct = env->GetMethodID(list_cls, "<init>", "()V"); //获得得构造函数Id

    jobject list_obj = env->NewObject(list_cls, list_costruct); //创建一个Arraylist集合对象
    //或得Arraylist类中的 add()方法ID,其方法原型为: boolean add(Object object) ;
    jmethodID list_add = env->GetMethodID(list_cls, "add", "(Ljava/lang/Object;)Z");
    jclass tpoint_cls = env->FindClass("com/medlander/candjavaobject/Tpoint");//获得Student类引用
    jmethodID tpoint_costruct = env->GetMethodID(tpoint_cls, "<init>", "(FF)V");
    for (int i = 0; i < 4; i++) {
        jobject tpoint_obj = env->NewObject(tpoint_cls, tpoint_costruct, student.tpoint[i].x,
                                            student.tpoint[i].y);  //构造一个对象
        env->CallBooleanMethod(list_obj, list_add, tpoint_obj); //执行Arraylist类实例的add方法,添加一个stu对象
        env->DeleteLocalRef(tpoint_obj);
    }
    env->SetObjectField(stu_obj, tpointFieldID, list_obj);
    env->DeleteLocalRef(list_cls);
    env->DeleteLocalRef(tpoint_cls);
    env->DeleteLocalRef(list_obj);
    env->DeleteLocalRef(stucls);
    return stu_obj;
}

一定要注意应用类型的资源手动释放,如:jclass,jobject,jbyteArray,jstring等。

log:

 D/jni_jokey: name: 小米
 D/jni_jokey: id: 12345678
 D/jni_jokey: age: 10
 D/jni_jokey: height: 135.199997
 D/jni_jokey: weight: 45.200001
 D/jni_jokey: score: 80.500000
 D/jni_jokey: sex: 女
 D/jni_jokey: arraylist_size: 4
 D/jni_jokey: i: 0
 D/jni_jokey: x: 1.000000
 D/jni_jokey: y: 1.000000
 D/jni_jokey: i: 1
 D/jni_jokey: x: 2.000000
 D/jni_jokey: y: 2.000000
 D/jni_jokey: i: 2
 D/jni_jokey: x: 3.000000
 D/jni_jokey: y: 3.000000
 D/jni_jokey: i: 3
 D/jni_jokey: x: 4.000000
 D/jni_jokey: y: 4.000000
 D/jokey: data: 896
 D/jokey: stu: Student{name='小米', id=12345678, age=10, height=135.2, weight=45.2, score=80.5, sex=false, tpoints=[Tpoint{x=1.0, y=1.0}, Tpoint{x=2.0, y=2.0}, Tpoint{x=3.0, y=3.0}, Tpoint{x=4.0, y=4.0}]}



版权声明:本文为yuanjj5549原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。