我的C/C++语言学习进阶之旅JNI开发之Java传递实体Bean到C++层,实体Bean包含intfloat等基本类型和数组arrayList集合等

Posted 字节卷动

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我的C/C++语言学习进阶之旅JNI开发之Java传递实体Bean到C++层,实体Bean包含intfloat等基本类型和数组arrayList集合等相关的知识,希望对你有一定的参考价值。

一、需求描述

最近有个需求,Java上层包装一系列的数据到一个实体bean,实体Bean包含int、float等基本类型和数组array、List集合等,然后通过JNI传递给C++层来进行数据处理,这边折腾了一番终于搞定,这里记录一下。

1.1 Java实体bean

首先,Java层实体bean代码如下:

package com.oyp.ndkdemo;

import android.graphics.PointF;

import java.util.List;

public class FaceFeatureBean 
    public int faceId;
    public PointF[] boundingBox;
    public PointF[] landmarks;
    public List<Float> visibilities;
    public float yaw;
    public float pitch;
    public float roll;

    public FaceFeatureBean(int faceId, PointF[] boundingBox, PointF[] landmarks, List<Float> visibilities, float yaw, float pitch, float roll) 
        this.faceId = faceId;
        this.boundingBox = boundingBox;
        this.landmarks = landmarks;
        this.visibilities = visibilities;
        this.yaw = yaw;
        this.pitch = pitch;
        this.roll = roll;
    

    public int getFaceId() 
        return faceId;
    

    public void setFaceId(int faceId) 
        this.faceId = faceId;
    

    public PointF[] getBoundingBox() 
        return boundingBox;
    

    public void setBoundingBox(PointF[] boundingBox) 
        this.boundingBox = boundingBox;
    

    public PointF[] getLandmarks() 
        return landmarks;
    

    public void setLandmarks(PointF[] landmarks) 
        this.landmarks = landmarks;
    

    public List<Float> getVisibilities() 
        return visibilities;
    

    public void setVisibilities(List<Float> visibilities) 
        this.visibilities = visibilities;
    

    public float getYaw() 
        return yaw;
    

    public void setYaw(float yaw) 
        this.yaw = yaw;
    

    public float getPitch() 
        return pitch;
    

    public void setPitch(float pitch) 
        this.pitch = pitch;
    

    public float getRoll() 
        return roll;
    

    public void setRoll(float roll) 
        this.roll = roll;
    


1.2 Java实体bean转成Kotlin实体bean

当然,上面的Java实体bean也可以转成Kotlin实体bean,代码简洁一些,如下所示:

package com.oyp.ndkdemo

import android.graphics.PointF

class FaceFeatureBean(
    var faceId: Int,
    var boundingBox: Array<PointF>,
    var landmarks: Array<PointF>,
    var visibilities: List<Float>,
    var yaw: Float,
    var pitch: Float,
    var roll: Float
)

二、JNI实现相关

2.1 编写Native方法入口

编写Native方法入口,如下所示:

    // Kotlin对外public的方法,用于业务调用
    fun setFaceFeature(feature: FaceFeatureBean) 
        nativeSetFaceFeatureBean(feature)
    

    // Native层方法
    private external fun nativeSetFaceFeatureBean(feature: FaceFeatureBean)

    // Kotlin对外public的方法,用于业务调用
    fun setFaceFeature2(feature: FaceFeatureBean) 
        nativeSetFaceFeatureBean2(feature)
    

    // Native层方法
    private external fun nativeSetFaceFeatureBean2(feature: FaceFeatureBean)

2.2 实现Native方法

这个方法的套路基本上是:

  1. 通过env->GetObjectClass拿到jclass
  2. 通过env->GetMethodID拿到jmethodID
  3. 通过env->CallIntMethod拿到属性
  4. 通过env->GetArrayLength拿到数组的长度,将拿到的就jobject对象转换为数组的指针jobjectArray *,然后遍历数组
  5. 拿到List集合之后,通过调用List的相关方法拿到Listsizeget方法,然后遍历List集合

代码如下:

extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_ndkdemo_JNI_nativeSetFaceFeatureBean(JNIEnv *env, jobject thiz, jobject feature) 
    LOGD("=================================Java_com_oyp_ndkdemo_JNI_nativeSetFaceFeatureBean=================================")
    // 获取FaceFeatureBean类
    jclass jFeature = env->GetObjectClass(feature);

    // 获取FaceFeatureBean对象的methodID
    jmethodID getFaceId = env->GetMethodID(jFeature, "getFaceId", "()I");
    jmethodID getYaw = env->GetMethodID(jFeature, "getYaw", "()F");
    jmethodID getPitch = env->GetMethodID(jFeature, "getPitch", "()F");
    jmethodID getRoll = env->GetMethodID(jFeature, "getRoll", "()F");

    // 执行方法 拿到属性
    jint faceId = env->CallIntMethod(feature, getFaceId);
    jfloat yaw = env->CallFloatMethod(feature, getYaw);
    jfloat pitch = env->CallFloatMethod(feature, getPitch);
    jfloat roll = env->CallFloatMethod(feature, getRoll);
    LOGD("faceId = %d,yaw = %f,pitch = %f,roll = %f", faceId, yaw, pitch, roll)

    jmethodID getVisibilities = env->GetMethodID(jFeature, "getVisibilities", "()Ljava/util/List;");
    jobject visibilities = env->CallObjectMethod(feature, getVisibilities);

    // 遍历 visibilities,拿到List的各个Item
    // 获取ArrayList对象
    jclass jcs_alist = env->GetObjectClass(visibilities);
    // 获取ArrayList的methodid
    jmethodID alist_get = env->GetMethodID(jcs_alist, "get", "(I)Ljava/lang/Object;");
    jmethodID alist_size = env->GetMethodID(jcs_alist, "size", "()I");
    jint len = env->CallIntMethod(visibilities, alist_size);
    for (int i = 0; i < len; i++) 
       // 获取 Float 对象
        jobject float_obj = env->CallObjectMethod(visibilities, alist_get, i);
        // 获取 Float 类
        jclass float_cls = env->GetObjectClass(float_obj);
        jmethodID getFloatValue = env->GetMethodID(float_cls, "floatValue", "()F");
        jfloat floatValue = env->CallFloatMethod(float_obj, getFloatValue);
        LOGD("visibilities列表中 第 %d 个值为:floatValue = %f", i + 1, floatValue)
    


    jmethodID getBoundingBox = env->GetMethodID(jFeature, "getBoundingBox",
                                                "()[Landroid/graphics/PointF;");
    jobject boundingBox = env->CallObjectMethod(feature,getBoundingBox);
    auto * boundingBoxArray = reinterpret_cast<jobjectArray *>(&boundingBox);

    const jint length = env->GetArrayLength(*boundingBoxArray);
    for(int i=0;i<length;i++)
        jobject point= env->GetObjectArrayElement(*boundingBoxArray,i);
        //1.获得实例对应的class类
        jclass jcls = env->GetObjectClass(point);
        //2.通过class类找到对应的field id
        //num 为java类中变量名,I 为变量的类型int
        jfieldID xID = env->GetFieldID(jcls,"x","F");
        jfieldID yID = env->GetFieldID(jcls,"y","F");

        jfloat x = env->GetFloatField(point,xID);
        jfloat y = env->GetFloatField(point,yID);
        LOGD("boundingBoxArray数组中 第 %d 个值为:x = %f , y = %f", i + 1, x,y)
    

    jmethodID getLandmarks = env->GetMethodID(jFeature, "getLandmarks",
                                              "()[Landroid/graphics/PointF;");
    jobject landmarks  = env->CallObjectMethod(feature,getLandmarks);
    auto * landmarksArray = reinterpret_cast<jobjectArray *>(&landmarks);
    const jint length2 = env->GetArrayLength(*landmarksArray);
    for(int i=0;i<length2;i++)
        jobject point= env->GetObjectArrayElement(*landmarksArray,i);
        //1.获得实例对应的class类
        jclass jcls = env->GetObjectClass(point);
        //2.通过class类找到对应的field id
        //num 为java类中变量名,I 为变量的类型int
        jfieldID xID = env->GetFieldID(jcls,"x","F");
        jfieldID yID = env->GetFieldID(jcls,"y","F");

        jfloat x = env->GetFloatField(point,xID);
        jfloat y = env->GetFloatField(point,yID);
        LOGD("landmarksArray 第 %d 个值为:x = %f , y = %f", i + 1, x,y)
    

如上所示,我们分了如下几个步骤来实现:

2.2.1. 获取FaceFeatureBean类

	// 获取FaceFeatureBean类
	jclass jFeature = env->GetObjectClass(feature);

2.2.2. 获取FaceFeatureBean对象的methodID

    // 获取FaceFeatureBean对象的methodID
    jmethodID getFaceId = env->GetMethodID(jFeature, "getFaceId", "()I");
    jmethodID getYaw = env->GetMethodID(jFeature, "getYaw", "()F");
    jmethodID getPitch = env->GetMethodID(jFeature, "getPitch", "()F");
    jmethodID getRoll = env->GetMethodID(jFeature, "getRoll", "()F");

2.2.3. 执行方法,拿到属性

 	// 执行方法 拿到属性
    jint faceId = env->CallIntMethod(feature, getFaceId);
    jfloat yaw = env->CallFloatMethod(feature, getYaw);
    jfloat pitch = env->CallFloatMethod(feature, getPitch);
    jfloat roll = env->CallFloatMethod(feature, getRoll);
    LOGD("faceId = %d,yaw = %f,pitch = %f,roll = %f", faceId, yaw, pitch, roll)

我们查看代码可知,实际上就是执行我标记的几个方法:getFaceId、getYaw、getPitch、getRoll

2.2.4. 解析List集合

首先执行getVisibilities方法,拿到集合对象

 	jmethodID getVisibilities = env->GetMethodID(jFeature, "getVisibilities", "()Ljava/util/List;");
    jobject visibilities = env->CallObjectMethod(feature, getVisibilities);

然后通过List对象,拿到get方法的methodID和size方法的methodID,执行size方法拿到List集合长度。

	// 遍历 visibilities,拿到List的各个Item
    // 获取ArrayList对象
    jclass jcs_alist = env->GetObjectClass(visibilities);
    // 获取ArrayList的methodid
    jmethodID alist_get = env->GetMethodID(jcs_alist, "get", "(I)Ljava/lang/Object;");
    jmethodID alist_size = env->GetMethodID(jcs_alist, "size", "()I");
    jint len = env->CallIntMethod(visibilities, alist_size);

get和size方法对应的java层代码如下


这样我们拿到了集合长度以及获取到了get方法methodID,那么就可以遍历集合了,完整如下所示:

    // 遍历 visibilities,拿到List的各个Item
    // 获取ArrayList对象
    jclass jcs_alist = env->GetObjectClass(visibilities);
    // 获取ArrayList的methodid
    jmethodID alist_get = env->GetMethodID(jcs_alist, "get", "(I)Ljava/lang/Object;");
    jmethodID alist_size = env->GetMethodID(jcs_alist, "size", "()I");
    jint len = env->CallIntMethod(visibilities, alist_size);
	for (int i = 0; i < len; i++) 
        // 获取Float对象
        jobject float_obj = env->CallObjectMethod(visibilities, alist_get, i);
        // 获取 Float类
        jclass float_cls = env->GetObjectClass(float_obj);
        jmethodID getFloatValue = env->GetMethodID(float_cls, "floatValue", "()F");
        jfloat floatValue = env->CallFloatMethod(float_obj, getFloatValue);
        LOGD("visibilities列表中 第 %d 个值为:floatValue = %f", i + 1, floatValue)
    

因为我们遍历的是List<Float>,所以拿到的每一个Item对象都是Float,接着我们要解析Float,拿到它的值,这时候,我们需要执行floatValue方法拿到它的值。

下面是执行floatValue方法,拿到值,接着打印的代码

 	// 获取Float对象
    jobject float_obj = env->CallObjectMethod(visibilities, alist_get, i);
    // 获取 Float类
    jclass float_cls = env->GetObjectClass(float_obj);
    jmethodID getFloatValue = env->GetMethodID(float_cls, "floatValue", "()F");
    jfloat floatValue = env->CallFloatMethod(float_obj, getFloatValue);
    LOGD("visibilities列表中 第 %d 个值为:floatValue = %f", i + 1, floatValue)

2.2.5. 解析对象数组

jmethodID getBoundingBox = env->GetMethodID(jFeature, "getBoundingBox",
                                                "()[Landroid/graphics/PointF;");
    jobject boundingBox = env->CallObjectMethod(feature,getBoundingBox);
    auto * boundingBoxArray = reinterpret_cast<jobjectArray *>(&boundingBox);

    const jint length = env->GetArrayLength(*boundingBoxArray);
    for(int i=0;i<length;i++)
        jobject point= env->GetObjectArrayElement(*boundingBoxArray,i);
        //1.获得实例对应的class类
        jclass jcls = env->GetObjectClass(point);
        //2.通过class类找到对应的field id
        //num 为java类中变量名,I 为变量的类型int
        jfieldID xID = env->GetFieldID(jcls,"x","F");
        jfieldID yID = env->GetFieldID(jcls,"y","F");

        jfloat x = env->GetFloatField(point,xID);
        jfloat y = env->GetFloatField(point,yID);
        LOGD("boundingBoxArray数组中 第 %d 个值为:x = %f , y = %f", i + 1, x,y)
    
  1. 首先我们通过getBoundingBox方法,拿到了数组对象。
 	jmethodID getBoundingBox = env->GetMethodID(jFeature, "getBoundingBox",
                                                "()[Landroid/graphics/PointF;");
    jobject boundingBox = env->CallObjectMethod(feature,getBoundingBox);
  1. 但是这个是jobject,而不是jobjectArray,因此我们需要强转一下,让它变成jobjectArray *指针
auto * boundingBoxArray = reinterpret_cast<jobjectArray *>(&boundingBox);
  1. 接着我们拿到数组的长度
    const jint length = env->GetArrayLength(*boundingBoxArray);
  1. 拿到了数组指针和数组长度,那我们就可以遍历了,如下所示:
	for(int i=0;i<length;i++)
        jobject point= env->GetObjectArrayElement(*boundingBoxArray,i);
        //1.获得实例对应的class类
        jclass jcls = env->GetObjectClass(point);
        //2.通过class类找到对应的field id
        //num 为java类中变量名,I 为变量的类型int
        jfieldID xID = env->GetFieldID(jcls,"x","F");
        jfieldID yID = env->GetFieldID(jcls,"y","F");

        jfloat x = env->GetFloatField我的C/C++语言学习进阶之旅JNI开发之转换C层返回的结构体为Java实体Bean

我的C/C++语言学习进阶之旅JNI开发之Java传递实体Bean到C++层,实体Bean包含intfloat等基本类型和数组arrayList集合等

我的C/C++语言学习进阶之旅JNI开发之Java传递实体Bean到C++层,实体Bean包含intfloat等基本类型和数组arrayList集合等

我的C/C++语言学习进阶之旅转载:实现一个在JNI中调用Java对象的工具类

我的C/C++语言学习进阶之旅转载:实现一个在JNI中调用Java对象的工具类

我的C/C++语言学习进阶之旅介绍一下NDK开发之C的简单易用图像库stb