NDK学习(一),向Native层传递各种参数、由Native层返回各种参数

向Native层传递各种参数、由 Native 层返回各种参数
 
本章涉及的技术要点:
a 、传递空参数,基本类型参数、多个参数, Java 自定义类对象、不同类型的数组做参数
b 、返回基本类型参数,返回 Java 自定义类对象,返回空
c 、在 JNI C 语言)环境中如何调用 Java 中的方法(得到方法 ID ,执行方法,得到返回值)
d 、在 JNI C 语言)环境如何获取某个 JAVA 自定义对象的成员变量(得到 Field ID, 获取值)
 
参考文档: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html
请先阅读章节: JNI Types and Data Structures
本章用到的 Java 代码:
 
1 、向 native 层传入多个多种参数,返回一个 Java 层的复杂对象(复杂对象中有另一个 Java 对象)
 
首先在Java 层定义 native 接口
public native ComplexClass generateComplexObject(int i,short s,long l,float f,double d,char c,byte byteVal,boolean b);
参数中共定义了8 中基本类型,以及要在 native 层利用这些参数返回一个 Complex 对象。
 
Native 实现:
JNIEXPORT jobject JNICALL
Java_dev_mars_jnidemo_NativeBridge_generateComplexObject(JNIEnv *env, jobject instance, jint i,
                                                         jshort s, jlong l, jfloat f, jdouble d,
                                                         jchar c, jbyte byteVal, jboolean b) {
    // TODO
    jclass simpleClass = env->FindClass("dev/mars/jnidemo/SimpleClass");
    jmethodID simpleClassBuildFunctionID = env->GetMethodID(simpleClass,"","(I)V");
    jobject scObj = env->NewObject(simpleClass,simpleClassBuildFunctionID,i);
    jfieldID shortField = env->GetFieldID(simpleClass,"shortVal","S");
    jfieldID doubleField = env->GetFieldID(simpleClass,"doubleVal","D");
    jfieldID floatField = env->GetFieldID(simpleClass,"floatVal","F");
    jfieldID longField = env->GetFieldID(simpleClass,"longVal","J");
    jfieldID charField = env->GetFieldID(simpleClass,"charVal","C");
    jfieldID byteField = env->GetFieldID(simpleClass,"byteVal","B");
    jfieldID boolField = env->GetFieldID(simpleClass,"boolVal","Z");
    env->SetLongField(scObj,longField,l);
    env->SetFloatField(scObj,floatField,f);
    env->SetDoubleField(scObj,doubleField,d);
    env->SetCharField(scObj,charField,c);
    env->SetByteField(scObj,byteField,byteVal);
    env->SetBooleanField(scObj,boolField,b);

    jclass complexClass =env->FindClass("dev/mars/jnidemo/ComplexClass");
    jmethodID complexClassBuildFunctionID = env->GetMethodID(complexClass,"","(Ldev/mars/jnidemo/SimpleClass;)V");
    jobject complexClassObj = env->NewObject(complexClass,complexClassBuildFunctionID,scObj);
    return complexClassObj;
}
 
 
要点1 jint jdouble 等基本类型可以作为 C 语言中的 int double 使用
通过GetMethodID 找到类的构造方法,第一个参数是方法名,但是构造方法是个特例,用 "" 表示,第二个参数是方法参数和返回类型的签名
 
要点2 :创建一个 Java 层对象,先要通过 FindClass 方法找到该类,参数是一个 包名 / 类名 的字符串,包名间的 . 号用 / 代替。
签名参考上述文档中的Type Signature 。如 Java 中对 SimpleClass 构造方法的定义,参数是一个 int 类型,因此签名为 (I), 构造方法的返回值为空,所以签名是 V ,组合在一起就是 (I)V ,如果构造方法有多个参数,每个参数签名用 ; 隔开,如果参数是引用类型则在后面必须加上 ; 无论后面有没有参数。
NewObject 方法通过 jclass jmethodID 、构造方法参数创建 jobject 对象。
 
要点3 :要直接修改对象的成员变量(该成员变量可以是 private 的,测试有效,类似 Java 反射),先调用 GetFieldID 方法获取成员变量 ID ,参数如 GetMethodID ,第一个参数是成员变量名,第二个是成员变量的签名。通过 SetShortField 等不同的方法为成员变量赋值。
要点四:在Native 层创建一个复杂对象(对象中包含另一个对象),所用的基本方法在要点 1 2 3 中有总结。
 
2、向native 层传入数组,并返回一个数组(数组类型以 int 为例,其他类型可以推导)
Java:
public native int[] getIntArray(int[] sourceArray);
Native:
JNIEXPORT jintArray JNICALL
Java_dev_mars_jnidemo_NativeBridge_getIntArray(JNIEnv *env, jobject instance,
                                               jintArray sourceArray_) {

    jint *sourceArray = env->GetIntArrayElements(sourceArray_, NULL);
    // TODO
    LOGE("print source array:");
    for (int i = 0; i < env->GetArrayLength(sourceArray_); i++) {
        LOGE("source array[%d] = %d", i, *(sourceArray + i));
    }
    env->ReleaseIntArrayElements(sourceArray_, sourceArray, 0);

    //generate new array
    jintArray newJintArray = env->NewIntArray(3);
    jint newArray[] = {1,2,3};
    env->SetIntArrayRegion(newJintArray,0,3,newArray);
    return newJintArray;
}
 
要点1:GetIntArrayElements可以将 jintArray 转换 jint * 指针,通过该指针可以打印传入的数组
要点2:要构造一个新的 jintArray 可以用 NewIntArray(jsize) 方法,然后定义一个 jint 数组,通过 SetIntArrayRegion 方法将 jint 数组赋值给 jintArray
 
3、通过 native 层改变 Java 对象的私有成员变量
public native PrivateFieldClass alterPrivateField(double d);

native层实现:
JNIEXPORT jobject JNICALL
Java_dev_mars_jnidemo_NativeBridge_alterPrivateField(JNIEnv *env, jobject instance, jdouble d) {
    jclass jclass1 = env->FindClass("dev/mars/jnidemo/PrivateFieldClass");
    jmethodID create = env->GetMethodID(jclass1,"","()V");
    jobject jobject1 = env->NewObject(jclass1,create);
    jfieldID privateField = env->GetFieldID(jclass1,"doubleVal","D");
    env->SetDoubleField(jobject1,privateField,d);
    return jobject1;
}
 
要点:jni 中对 private 成员变量的修改一样是有效的

你可能感兴趣的:(NDK)