07_JNI Field Method Constructor_method Exception Memory

#define _CRT_SECURE_NO_WARNINGS
#include "com_dongnaoedu_jni_JniTest.h"
#include <string.h>
#include <stdlib.h>


//#include <Windows.h>


//函數實現
JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_getStringFromC
(JNIEnv *env, jclass jcls){
//WinExec("C:\\Program Files (x86)\\Tencent\\QQ\\Bin\\QQScLauncher.exe",0);


//JNIEnv 結構體指針
//env二級指針
//代表Java運行環境,調用Java中的代碼
//簡單的實現
//將C的字符串轉爲一個java字符串
return (*env)->NewStringUTF(env,"C String");
}


JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_getString2FromC
(JNIEnv *env, jobject jobj, jint num){
return (*env)->NewStringUTF(env,"C String2");
}


//每個native函數,都至少有兩個參數(JNIEnv*,jclass或者jobject)
//1)當native方法爲靜態方法時:
//jclass 代表native方法所屬類的class對象(JniTest.class)
//2)當native方法爲非靜態方法時:
//jobject 代表native方法所屬的對象


//基本數據
//Java基本數據類型與JNI數據類型的映射關係
//Java類型->JNI類型->C類型


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


//引用類型(對象)
//String jstring
//object jobject
//數組,基本數據類型的數組
//byte[] jByteArray
//對象數組
//object[](String[]) jobjectArray


//C/C++訪問Java的成員


//1.訪問屬性
//修改屬性key
JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_accessField
(JNIEnv *env, jobject jobj){
//jobj是t對象,JniTest.class
jclass cls = (*env)->GetObjectClass(env, jobj);
//jfieldID
//屬性名稱,屬性簽名
jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");


//jason >> super jason
//獲取key屬性的值
//Get<Type>Field
jstring jstr = (*env)->GetObjectField(env, jobj, fid);
printf("jstr:%#x\n",&jstr);


//jstring -> c字符串
//isCopy 是內存是否對該字符串進行復制的結果反饋(如果jni進行復制了就是jni_true 那就是兩個數據地址  否則就是jni_false 同一個數據)
char *c_str = (*env)->GetStringUTFChars(env,jstr,JNI_FALSE);
//拼接得到新的字符串
char text[20] = "super ";
strcat(text,c_str);


//c字符串 ->jstring
jstring new_jstr = (*env)->NewStringUTF(env, text);


//修改key
//Set<Type>Field
(*env)->SetObjectField(env, jobj, fid, new_jstr);


printf("new_jstr:%#x\n", &new_jstr);


return new_jstr;
}


//訪問靜態屬性
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_accessStaticField
(JNIEnv *env, jobject jobj){
//jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
//jfieldID
jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I");
//GetStatic<Type>Field
jint count = (*env)->GetStaticIntField(env, cls, fid);
count++;
//修改
//SetStatic<Type>Field
(*env)->SetStaticIntField(env,cls,fid,count);
}


//2.訪問java方法
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_accessMethod
(JNIEnv *env, jobject jobj){
//jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
//jmethodID
jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");
//調用
//Call<Type>Method
jint random = (*env)->CallIntMethod(env, jobj, mid, 200);
printf("random num:%ld",random);


//.....
}


//靜態方法
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_accessStaticMethod
(JNIEnv *env, jobject jobj){
//jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
//jmethodID
jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");

//調用
//CallStatic<Type>Method
jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);


//隨機文件名稱 uuid.txt
//jstring -> char*
//isCopy JNI_FALSE,代表java和c操作的是同一個字符串
char *uuid_str = (*env)->GetStringUTFChars(env, uuid, JNI_FALSE);
//拼接
char filename[100];
sprintf(filename, "D://%s.txt",uuid_str);
FILE *fp = fopen(filename,"w");
fputs("i love jason", fp);
fclose(fp);
}


//訪問構造方法
//使用java.util.Date產生一個當前的時間戳
JNIEXPORT jobject JNICALL Java_com_dongnaoedu_jni_JniTest_accessConstructor
(JNIEnv *env, jobject jobj){
jclass cls = (*env)->FindClass(env, "java/util/Date");
//jmethodID
jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
//實例化一個Date對象
jobject date_obj = (*env)->NewObject(env, cls, constructor_mid);
//調用getTime方法
jmethodID mid = (*env)->GetMethodID(env, cls, "getTime", "()J");
jlong time = (*env)->CallLongMethod(env, date_obj, mid);


printf("\ntime:%lld\n",time);


return date_obj;
}


//調用父類的方法
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_accessNonvirtualMethod
(JNIEnv *env, jobject jobj){
jclass cls = (*env)->GetObjectClass(env, jobj);
//獲取man屬性(對象)
jfieldID fid = (*env)->GetFieldID(env, cls, "human", "Lcom/dongnaoedu/jni/Human;");
//獲取
jobject human_obj = (*env)->GetObjectField(env, jobj, fid);


//執行sayHi方法
jclass human_cls = (*env)->FindClass(env, "com/dongnaoedu/jni/Human"); //注意:傳父類的名稱
jmethodID mid = (*env)->GetMethodID(env, human_cls, "sayHi", "()V");


//執行
//(*env)->CallObjectMethod(env, human_obj, mid);
//調用的父類的方法
(*env)->CallNonvirtualObjectMethod(env, human_obj, human_cls, mid);
}


//中文問題
JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_chineseChars
(JNIEnv *env, jobject jobj, jstring in){
//輸出  ------java中的編碼要爲UTF-8編碼 否則這裏獲取到的中文也會是亂碼
//char *c_str = (*env)->GetStringUTFChars(env, in, JNI_FALSE);
//printf("%s\n",c_str);


//c -> jstring
char *c_str = "等我打完這盤遊戲就回家吃飯!";
//char c_str[] = "等我打完這盤遊戲就回家吃飯!";
//jstring jstr = (*env)->NewStringUTF(env, c_str);
//執行String(byte bytes[], String charsetName)構造方法需要的條件
//1.jmethodID
//2.byte數組
//3.字符編碼jstring


jclass str_cls = (*env)->FindClass(env, "java/lang/String");
jmethodID constructor_mid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V");


//jbyte -> char 
//jbyteArray -> char[]
jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str));
//byte數組賦值
//0->strlen(c_str),從頭到尾
//對等於,從c_str這個字符數組,複製到bytes這個字符數組
(*env)->SetByteArrayRegion(env, bytes, 0, strlen(c_str), c_str);


//字符編碼jstring
jstring charsetName = (*env)->NewStringUTF(env, "GB2312");


//調用構造函數,返回編碼之後的jstring
return (*env)->NewObject(env,str_cls,constructor_mid,bytes,charsetName);
}


int compare(int *a,int *b){
return (*a) - (*b);
}


//傳入
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_giveArray
(JNIEnv *env, jobject jobj, jintArray arr){
//jintArray -> jint指針 -> c int 數組
jint *elems = (*env)->GetIntArrayElements(env, arr, NULL);
//printf("%#x,%#x\n", &elems, &arr);


//數組的長度
int len = (*env)->GetArrayLength(env, arr);
//排序
qsort(elems, len, sizeof(jint), compare);


//同步
//mode
//0, Java數組進行更新,並且釋放C/C++數組
//JNI_ABORT  -脫離, Java數組不進行更新,但是釋放C/C++數組
//JNI_COMMIT-提交 ,Java數組進行更新,不釋放C/C++數組(函數執行完,數組還是會釋放)
(*env)->ReleaseIntArrayElements(env, arr, elems, JNI_COMMIT);
}


//返回數組
JNIEXPORT jintArray JNICALL Java_com_dongnaoedu_jni_JniTest_getArray(JNIEnv *env, jobject jobj, 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;
}




//JNI 引用變量
//引用類型:局部引用和全局引用
//作用:在JNI中告知虛擬機何時回收一個JNI變量


//局部引用,通過DeleteLocalRef手動釋放對象
//1.訪問一個很大的java對象,使用完之後,還要進行復雜的耗時操作
//2.創建了大量的局部引用,佔用了太多的內存,而且這些局部引用跟後面的操作沒有關聯性


//模擬:循環創建數組
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_localRef(JNIEnv *env, jobject jobj){
int i = 0;
for (; i < 5; i++){
//創建Date對象
jclass cls = (*env)->FindClass(env, "java/util/Date");
jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
jobject obj = (*env)->NewObject(env, cls, constructor_mid);
//此處省略一百行代碼...


//不在使用jobject對象了
//通知垃圾回收器回收這些對象
(*env)->DeleteLocalRef(env, obj);
//此處省略一百行代碼...
}
}




//全局引用
//共享(可以跨多個線程),手動控制內存使用
jstring global_str;


//創建
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_createGlobalRef(JNIEnv *env, jobject jobj){
jstring obj = (*env)->NewStringUTF(env, "jni development is powerful!");

global_str = (*env)->NewGlobalRef(env, obj);

//(*env)->DeleteLocalRef(env, obj);  //清除不用的locObj
}


//獲得
JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_getGlobalRef(JNIEnv *env, jobject jobj){
return global_str;
}


//釋放
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_deleteGlobalRef(JNIEnv *env, jobject jobj){
(*env)->DeleteGlobalRef(env, global_str);
}


//弱全局引用
//節省內存,在內存不足時可以是釋放所引用的對象
//可以引用一個不常用的對象,如果爲NULL,臨時創建
//創建:NewWeakGlobalRef,銷燬:DeleteGlobalWeakRef


//異常處理
//1.保證Java代碼可以運行
//2.補救措施保證C代碼繼續運行


//JNI自己拋出的異常,在Java層可以用Error 或者ThrowAble 捕捉 該問題屬於錯誤不能用Exception捕獲
//用戶通過ThrowNew拋出的異常,可以在Java層捕捉
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_exeception(JNIEnv *env, jobject jobj){
jclass cls = (*env)->GetObjectClass(env, jobj);
jfieldID fid = (*env)->GetFieldID(env, cls, "key2", "Ljava/lang/String;");
//檢測是否發生Java異常
jthrowable exception = (*env)->ExceptionOccurred(env);
if (exception != NULL){
//讓Java代碼可以繼續運行
//清空異常信息
(*env)->ExceptionClear(env);


//補救措施
fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
}


//獲取屬性的值
jstring jstr = (*env)->GetObjectField(env, jobj, fid);
char *str = (*env)->GetStringUTFChars(env, jstr, NULL);


//對比屬性值是否合法
if (_stricmp(str, "super") != 0){
//認爲拋出異常,給Java層處理
jclass newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
(*env)->ThrowNew(env,newExcCls,"key's value is invalid!");
}
}


//緩存策略
//static jfieldID key_id 
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_cached(JNIEnv *env, jobject jobj){
jclass cls = (*env)->GetObjectClass(env, jobj);
//獲取jfieldID只獲取一次
//局部靜態變量
static jfieldID key_id = NULL;
if (key_id == NULL){
key_id = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
printf("--------GetFieldID-------\n");
}
}


//初始化全局變量,動態庫加載完成之後,立刻緩存起來
jfieldID key_fid;
jmethodID random_mid;
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_initIds(JNIEnv *env, jclass jcls){
key_fid = (*env)->GetFieldID(env, jcls, "key", "Ljava/lang/String;");
random_mid = (*env)->GetMethodID(env, jcls, "genRandomInt", "(I)I");
}
發佈了41 篇原創文章 · 獲贊 19 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章