向Native层传递各种参数、由Native层返回各种参数
本章涉及的技术要点:
a、传递空参数,基本类型参数、多个参数,Java自定义类对象、不同类型的数组做参数
b、返回基本类型参数,返回Java自定义类对象,返回空
c、在JNI(C语言)环境中如何调用Java中的方法(得到方法ID,执行方法,得到返回值)
d、在JNI(C语言)环境如何获取某个JAVA自定义对象的成员变量(得到Field
ID,获取值)
请先阅读章节: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,"<init>","(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,"<init>","(Ldev/mars/jnidemo/SimpleClass;)V");
jobject complexClassObj = env->NewObject(complexClass,complexClassBuildFunctionID,scObj);
return complexClassObj;
}
要点1:jint、jdouble等基本类型可以作为C语言中的int、double使用
通过GetMethodID找到类的构造方法,第一个参数是方法名,但是构造方法是个特例,用"<init>"表示,第二个参数是方法参数和返回类型的签名
要点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,"<init>","()V");
jobject jobject1 = env->NewObject(jclass1,create);
jfieldID privateField = env->GetFieldID(jclass1,"doubleVal","D");
env->SetDoubleField(jobject1,privateField,d);
return jobject1;
}
要点:jni中对private成员变量的修改一样是有效的
|
|