Android NDK – JNI 传递自定义类型及其数组传递

  • Post author:
  • Post category:其他

在实际开发中,JNI与java有很多场景需要传递数据,有时是基本数据类型,而有时则是自定义类型。在遇到通过回调Java中函数进行自定义类型传递时,是不可以直接传递,需要进行转换,本篇将针对参数存在自定义类型和自定义类型数组两种场景进行举例说明。

基本数据类型参数数组

针对如下代码片段说明:

//基本数据类型参数数组的静态函数
package com.test.jni;
//Java 函数
public class TestFunc{
    public static void testFunc(float vals[]){
        //...
    }
}

//JNI函数回调片段
jclass clz = jenv->FindClass("com/test/jni/TestFunc");
jmethodID methodId = env->GetMethodID(clz,"TestFunc","([F)V");
float[] data = new float[]{1.f,2.f};
//!!错误示例!!
env->CallStaticVoidMethod(clz,methodId,data);
//正确用例
jfloatArray array = env->NewFloatArray(2);
//参数1:待赋值数组;参数2:赋值起始位置;参数3:赋值长度;参数4:数据源
env->SetFloatArrayRegion(array,0,3,data);
env->CallStaticVoidMethod(clz,methodId,array);

自定义结构类型参数数组

针对如下代码片段说明:

//自定义类型
package com.test.jni;
public class MyObject{
    private String name;
    private int number;
    //some getters and setters
    ...
    public MyObject(String name, int number){
        this.name = name;
        this.number = number;
    }
}

//自定义类型参数数组的静态函数
package com.test.jni;
//Java 函数
public class TestFunc{
    public static void testFunc(MyObject vals[]){
        //...
    }
}

//JNI函数回调片段
jclass clz = jenv->FindClass("com/test/jni/TestFunc");
jmethodID methodId = jenv->GetMethodID(clz,"TestFunc","([Lcom/test/jni/MyObject;)V");
MyObject myObjects = new MyObject[2];
for(int i = 0; i < myObjects.length; i++){
    myObjects[i] = new MyObject("name_" + i, i);
}
//!!错误示例!!
jenv->CallStaticVoidMethod(clz,methodId,myObjects);
//正确用例: 需要在JNI层创建类型数组
jclass jclzMyObject = jenv->FindClass("com/test/jni/MyObject");
jmethodID jmethodConstructID = jenv->GetMethodID(jclzMyObject, "<init>", "()V");
//进行实例创建
jobjectArray array = jenv->NewObjectArray(myObjects.length, jclzMyObject,NULL);
for(int i = 0; i < 2; i++){
    jobject jobjMyObj = jenv->NewObject(jclzMyObject, jmethodConstructID);
    jfieldID nameField = jenv->GetFieldID(jclzMyObject, "name","Ljava/lang/String;");
    jfieldID numberField = jenv->GetFieldID(jclzMyObject,, "number","I");
    jenv->SetObjectField(jobjMyObj, nameField, myObjects[i].name);
    jenv->SetIntField(jobjMyObj, numberField, myObjects[i].number);
    jenv->SetObjectArrayElement(array, i, jobjMyObj);
    jenv->DeleteLocalRef(jobjMyObj);
}

//调用类的静态方法
jenv->CallStaticVoidMethod(clz,methodId,array);

通过在JNI层重新创建实例的数组,并将数组进行赋值,此时可以通过该反射调用Java函数的方式将JNI层的数值传递到Java层进行后续的计算处理。有以下需要注意的地方:

  • 在JNI层创建实例对象时,需要对Java层该类的构造函数进行有针对性的实现;
  • 在JNI层创建实例对象之后,尤其是还需要加入其数组数据结构中,需要在最后进行本地引用的释放,否则在循环创建超过512个引用之后则会溢出;
  • 针对非静态方法,则需要类似地通过反射在JNI层创建类的实例,通过调用实例函数方式;

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