1. 程式人生 > >jni系列 C層訪問Java層的方法(四)

jni系列 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++的陣列記憶體。


作者:JerryloveEmily
連結:https://juejin.im/post/5b5b18d0518825597f6b8226
來源:掘金
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。