JNI系列文章:
- JNI系列之入门Hello JNI C(一)
- JNI系列之入门Hello JNI C(二)
- JNI系列入门之C语言与Java的双向通信(一)
- JNI系列入门之C语言与Java的双向通信(二)
- JNI系列入门之C语言中文字符串乱码问题
Jerry哥人狠话不多,直接讲正题。
C层向Java层通信
- C层访问Java层的方法
// java代码
/*
* 在C中调用次方法,获取登入的用户id
*/
private String getLoginUserId(){
return "100010";
}
// c代码
// 3. 访问java方法
JNIEXPORT void JNICALL Java_com_jerry_jnitest_JniTest_accessMethod
(JNIEnv *env, jobject jobj) {
// 获取jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
// 获取调用方法的jmethodID
jmethodID methodID = (*env)->GetMethodID(env, cls, "getLoginUserId", "()Ljava/lang/String;");
// 调用获取登录用户id的方法
jint value = (*env)->CallIntMethod(env, jobj, methodID);
printf("userId = %s\n", value);
}
- C层访问Java层的静态方法
// java代码
// c中调用的java静态方法
/**
* 获取文件的大小
* @param pathName
* @return 文件大小
*/
public static long getFileSize(String pathName){
return new File(pathName).length();
}
// 创建和写入一个字符串到文件中,并返回文件大小
JNIEXPORT jlong JNICALL Java_com_jerry_jnitest_JniTest_createAndWriteFile
(JNIEnv *env, jobject jobj, jstring jstr_file_path) {
// 写入的文件路径 jstring->c
char *filename = (*env)->GetStringUTFChars(env, jstr_file_path, NULL);
printf("filename: %s\n", filename);
// 创建一个文件, "w"表示写入权限,文件存在则覆盖
FILE *fp = fopen(filename, "w");
char *text = "在C中创建一个文件并写入内容,并返回文件大小";
// c的文件io函数,写入文件
fputs(text, fp);
// 关闭流
fclose(fp);
// 计算文件的长度,这里不用c的函数来计算,改用调用java的文件api的方式来计算
// 获取getFileSize方法所在类的类类型
jclass jcls = (*env)->GetObjectClass(env, jobj);
// 获取方法的id
jmethodID mid = (*env)->GetStaticMethodID(env, jcls, "getFileSize",
"(Ljava/lang/String;)J");
// 调用方法
jlong file_size = (*env)->CallStaticLongMethod(env, jcls, mid, jstr_file_path);
printf("file_size: %lld", file_size);
// 释放filename
(*env)->ReleaseStringUTFChars(env, jstr_file_path, filename);
return file_size;
}
这里我通过C调用了java的文件计算api,因为java计算文件比较简单,直接File.length()就好了。当然也可以用c来实现获取文件的长度大小:
// 4. 获取文件大小
int filesize(FILE *fp) {
int length = 0;
if (fp == NULL) {
return length;
}
// 将文件指针的位置,重新定位文件指针
// 0是偏移量,SEEK_END表示文件的末尾位置
fseek(fp, 0, SEEK_END);
// 返回当前文件指针,相对于文件开头的位置偏移量,就是文件字节长度
length = ftell(fp);
printf("length = %d\n", length);
return length;
}
小伙伴们肯定会有疑问,你这方法的签名,记不住啊,容易懵逼啊。没有关系,我们还可以用命令的方式生成:
javap -s -p 类的完整名称,可以自动生成属性和方法签名。- C层访问Java层的构造方法,并创建Java对象返回
// 5. 访问java的构造方法
// 使用java.util.Date获得一个时间戳
JNIEXPORT jobject JNICALL Java_com_jerry_jnitest_JniTest_accessConstructor
(JNIEnv *env, jobject jobj) {
// 获取jclass
jclass cls = (*env)->FindClass(env, "java/util/Date");
// 获取jmethodID, 构造方法的名字使用<init>
jmethodID contructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
// 调用构造方法,实例化一个Date对象
jobject date_jobj = (*env)->NewObject(env, cls, contructor_mid);
// 调用Date的getTime方法
// 获取date的jclass
jclass date_cls = (*env)->GetObjectClass(env, date_jobj);
// 获取getTime的jmethodID
jmethodID getTime_mid = (*env)->GetMethodID(env, date_cls, "getTime", "()J");
// 调用getTime方法
jlong timestamp_jlong = (*env)->CallLongMethod(env, date_jobj, getTime_mid);
printf("timestamp = %lld\n", timestamp_jlong);
return date_jobj;
}
构造方法比较特殊,在获取jmethodID的时候传入的方法名是固定的。
- java中传入数组
// 8. java传入一个数组进行排序后同步
// 比较两个数的大小
int compare(int *a, int *b) {
return (*a) - (*b);
}
JNIEXPORT void JNICALL Java_com_jerry_jnitest_JniTest_inputSortArray
(JNIEnv *env, jobject jobj, jintArray jintArr) {
// 获取数组中的元素jint*
jint *int_arr = (*env)->GetIntArrayElements(env, jintArr, NULL);
// 获取数组长度
int length = (*env)->GetArrayLength(env, jintArr);
// 利用快速排序法排列数组
qsort(int_arr, length, sizeof(jint), compare);
// 同步操作后的数组内存到java中
// 最后一个参数mode的解释
// 0:Java的数组更新同步,然后释放C/C++的数组内存
// JNI_ABORT:Java的数组不会更新同步,但是释放C/C++的数组内存
// JNI_COMMIT:Java的数组更新同步,不释放C/C++的数组内存(但是函数执行完了局部的变量还是会释放掉)
(*env)->ReleaseIntArrayElements(env, jintArr, int_arr, 0);
}
ReleaseIntArrayElements这个函数很重要,只有调用了这个函数,C中操作的数组元素,才会同步到java,否则java中传入的数组打印后还是原来的。
- C中生成一个数组返回给java
// 9. C中生成一个数组返回给java
JNIEXPORT jintArray JNICALL Java_com_jerry_jnitest_JniTest_outputArray
(JNIEnv *env, jobject jobj, jint len) {
// 创建一个jintArray数组变量
jintArray jint_Arr = (*env)->NewIntArray(env, len);
// 将jintArray转换成c的jint*指针进行数组赋值
jint *elements = (*env)->GetIntArrayElements(env, jint_Arr, NULL);
int i = 0;
for (; i < len; i++) {
elements[i] = i;
}
// 将C中的创建修改的数组同步到Java中
(*env)->ReleaseIntArrayElements(env, jint_Arr, elements, 0);
return jint_Arr;
}
同样也要调用ReleaseIntArrayElements函数,去同步操作的数据,java获取的数组才会有值,同时释放掉C/C++的数组内存。
JNI系列文章: