java和jni交互 C語言與Java的雙向通信(二)互傳數組

原文鏈接:https://juejin.im/post/5b5b18d0518825597f6b8226

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;
}
複製代碼

小夥伴們肯定會有疑問,你這方法的簽名,記不住啊,容易懵逼啊。沒有關係,我們還可以用命令的方式生成:

生成java的方法簽名

 

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++的數組內存。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章