美文网首页
NDK—JNI的调用

NDK—JNI的调用

作者: Lypop | 来源:发表于2017-10-18 23:24 被阅读0次

上一文我们讲了jni的简单写法,现在我们将深入讲解java和C语言之间属性和方法之间的来回调用,这里我们就不再说怎么引入jni.h和jni_md.h等等

在开始之前我们来了解一下jni的基本数据类型,java基本类型与JNI数据类型的映射关系:Java类型->JNI类型->C类型

boolean jboolean
byte jbyte;
char jchar;
short jshort;
int jint;
long jlong;
float jfloat;
double jdouble;
void void

这里只是列举了java类型和JNI类型的对应,还有其它的类型

String jstring
object jobject
byte[] jByteArray
object[] jobjectArray

正文

对于java和C语言之间的属性和方法调用,我们分两个模块来讲解,分别是属性和方法

属性

C语言在访问属性的时候不会直接去访问,而是java程序去调用C语言,然后C语言去修改java中的属性

// 访问属性,返回修改之后的属性内容
public native String accessField();

然后在.h里面去声明在.c里面去实现,该实现方法

JNIEXPORT jstring JNICALL Java_com_lypop_JniDemo_accessField
(JNIEnv *env, jobject obj){
jclass cls = (*env)->GetObjectClass(env, obj);
jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
jstring jstr = (*env)->GetObjectField(env, obj, fid);

jboolean isCopy = NULL;
char *c_str = (*env)->GetStringUTFChars(env, jstr, &isCopy);

char text[20] = "super ";
strcat(text, c_str);

jstring new_jstr = (*env)->NewStringUTF(env, text);

//修改key
(*env)->SetObjectField(env, obj, fid, new_jstr);

//释放字符串资源
(*env)->ReleaseStringUTFChars(env, jstr, c_str);
return new_jstr;

}

tips:

  1. GetObjectClass根据对象得到类的class对象
  2. GetFieldID根据class的对象来获取字段的ID,第三个参数是字段的名字,第四个参数是字段的签名,对于这种可以使用下面的表格来进行查找


当然你也可以使用javap命令来查看


  1. GetStringUTFChars最后一个参数为是否进行copy,在API中解释到当isCopy不为空的时候,如果将其设置为JNI_TRUE则进行复制一份,如果为JNI_FALSE则不进行复制在原有的值进行操作,我们应该将isCopy设置为NULL,当执行该方法之后通过判断isCopy来确定是否进行了复制
  2. API也有介绍到当调用GetStringUTFChars方法会返回一个指针指向一个数组,这个数组是有效的在调用ReleaseStringUTFChars之前,所以当我们调用完之后需要进行释放资源
  3. 接下来通过GetObjectField方法得到jstring,然后调用GetStringUTFChars方法得到char,通过改变char,接着调用NewStringUTF生成jstring,最后调用SetObjectField将最新的jstring设置回去

当然你也可以去修改java里的静态属性,代码和上面的类似只是方法不一样

jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I");
jint count = (*env)->GetStaticIntField(env, cls, fid);
count++;
//修改
(*env)->SetStaticIntField(env, cls, fid, count);
方法

这里我们列举几种例子
简单调用java中的方法

JNIEXPORT void JNICALL   
Java_com_lypop_JniDemo_accessMethod
(JNIEnv *env, jobject obj){
jclass cls = (*env)->GetObjectClass(env,obj);

jmethodID mid = (*env)->GetMethodID(env, cls,"genRandomInt","(I)I");

jint random = (*env)->CallIntMethod(env, obj, mid);

printf("random num:%d", random);

}

流程和调用java字段类似,先GetObjectClass生成jclass对象然后GetMethodID生成jmethod的ID,最后CallIntMethod调用相应的方法

接下来就是调用相应的静态的方法

jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");
//调用
jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);
//随机文件名称uuid.txt
char *uuid_str = (*env)->GetStringUTFChars(env, uuid, NULL);
//拼接
char filename[100];
sprintf(filename,"D://%s.txt",uuid_str);
FILE *fp = fopen(filename, "w");

fputs("i love hua", fp);
fclose(fp);

因为代码和上一个差不多,这里就不多说,这里需要说一下方法的签名构成:括号代表方法的参数没值说明无参数,后面代表该方法的返回值

调用java带参数的方法

JNIEXPORT void JNICALL Java_com_lypop_JniDemo_giveArray
(JNIEnv *env, jobject obj, jintArray arr){
jint *elems = (*env)->GetIntArrayElements(env, arr, NULL);
//数组的长度
int len = (*env)->GetArrayLength(env, arr);
//对数组进行排序
qsort(elems, len, sizeof(jint), compare);
//同步
//释放数组的元素
(*env)->ReleaseIntArrayElements(env, arr, elems, JNI_ABORT);

}

tips:

1.ReleaseIntArrayElements方法 mode:0时,java数组进行更新,并释放资源;mode为JNI_ABORT时,java数组不进行更新,释放资源;mode为JNI_COMMIT时,java数组进行更新,不释放资源

有返回值的方法

JNIEXPORT jintArray JNICALL Java_com_lypop_JniDemo_getArray
(JNIEnv *env, jobject obj,jint len){
jintArray jint_arr = (*env)->NewIntArray(env, len);
//对数组进行赋值
jint *elems = (*env)->GetIntArrayElements(env, jint_arr, NULL);
int i = 0;
//循环赋值
for (; i < len; i++){
    elems[i] = i;
}
//同步
(*env)->ReleaseIntArrayElements(env, jint_arr, elems, 0);
return jint_arr;
}

相关文章

网友评论

      本文标题:NDK—JNI的调用

      本文链接:https://www.haomeiwen.com/subject/oleyuxtx.html