一、JNI訪問java數(shù)組
-
1.JNI訪問數(shù)組并使用qsort快速排序
JNIEXPORT void JNICALL Java_com_Jni_1Test_giveArray (JNIEnv * env, jobject jobj, jintArray arr) { //申明排序用的比較函數(shù) int compare(jint * a, jint *b); //java數(shù)組轉(zhuǎn)換為JNI數(shù)組 jint *elemts = (*env)->GetIntArrayElements(env, arr, NULL); if (elemts == NULL){return;}//非空判斷 //獲取java數(shù)組長度 int len = (*env)->GetArrayLength(env, arr); //qsortC語言快速排序函數(shù) //elemts:需要排序的數(shù)組 //len:數(shù)組長度 //sizeof(jint):數(shù)組元素的寬度 //compare:比較函數(shù) qsort(elemts, len, sizeof(jint), compare); //JNI操作 釋放可能的內(nèi)存 將JNI修改的數(shù)據(jù)重新寫回原來的內(nèi)存 (*env)->ReleaseIntArrayElements(env, arr, elemts, JNI_COMMIT); } //C語言中 布爾型的定義:所有>0的都為true,<=0為false int compare(jint * a, jint *b) { return *a - *b; }
-
2.jni訪問引用類型的數(shù)組
/* * java中定義JNI方法 * public native String[] initStringArray(int size); */ JNIEXPORT jobjectArray JNICALL Java_com_hubin_Jni_1Test_initStringArray (JNIEnv * env, jobject jobj, jint size) { //創(chuàng)建jobjectArray 數(shù)組 jobjectArray result; jclass jclz; int i; //反射找到對應(yīng)java對象 jclz = (*env)->FindClass(env, "java/lang/String"); if (jclz == NULL) {return NULL;} result = (*env)->NewObjectArray(env, size, jclz, jobj); if (result == NULL) {return NULL;} //賦值 for (i = 0; i < size; i++) { //C字符串 char * c_str = (char *)malloc(256); memset(c_str, 0, 256); //將int 轉(zhuǎn)換成為char sprintf(c_str, "hello num: %d\n", i); //C ->jstring jstring str = (*env)->NewStringUTF(env, c_str); if (str == NULL) {return NULL;} //將jstring賦值給數(shù)組 (*env)->SetObjectArrayElement(env, result, i, str); free(c_str); //釋放 c_str = NULL; //(*env)->DeleteGlobalRef(env, str); } //返回jobjectArray return result; }
二底瓣、引用
- 1.局部引用
// 定義方式多樣:FindClass谢揪,NewObject,GetObjectClass,NewCharArray.... NewLocalRef() 這些都是引用
//釋放方式: 1 方法調(diào)用完JVM 會自動釋放 2.DeleteLocalRef
// 不能在多線程里面使用
JNIEXPORT void JNICALL Java_com_hubin_Jni_1Test_localRef
(JNIEnv * env, jobject jobj) {
int i = 0;
for (i = 0; i < 5; i++)
{
jclass cls = (*env)->FindClass(env, "java/util/Date");
jmethodID jmid = (*env)->GetMethodID(env, cls, "<init>", "()V");
//創(chuàng)建一個Date類型的局部引用
jobject obj = (*env)->NewObject(env, cls, jmid);
//使用這個引用
//釋放引用(JNI局部引用表里面最多存放512個引用,所以要及時釋放避免引用溢出)
(*env)->DeleteLocalRef(env, cls);
(*env)->DeleteLocalRef(env, obj);
}
}
-
2.全局引用
/* * java中native方法 * 創(chuàng)建: public native void createGlobalRef(); * 獲染杵尽: public native String getGlobalRef(); * 釋放: public native void delGlobalRef(); */ //跨線程拨扶,跨方法使用 // NewGlobalRef 是創(chuàng)建全局引用的唯一方法 jstring global_str; JNIEXPORT void JNICALL Java_com_hubin_Jni_1Test_createGlobalRef (JNIEnv * env, jobject jobj) { jobject obj = (*env)->NewStringUTF(env, "JNI is intersting"); global_str = (*env)->NewGlobalRef(env, obj); //創(chuàng)建全局引用的唯一方法 } JNIEXPORT jstring JNICALL Java_com_hubin_Jni_1Test_getGlobalRef (JNIEnv * env, jobject jobj) { return global_str; } JNIEXPORT void JNICALL Java_com_hubin_Jni_1Test_delGlobalRef (JNIEnv * env, jobject jobj) { (*env)->DeleteGlobalRef(env, global_str); }
-
3.弱全局引用
//弱全局引用類(似于java中的弱引用) //它不會阻止GC,/跨線程,跨方法使用 jclass g_weak_cls; JNIEXPORT jstring JNICALL Java_com_hubin_Jni_1Test_createWeakRef (JNIEnv * env, jobject jobj) { jclass cls_string = (*env)->FindClass(env, "java/lang/String"); g_weak_cls = (*env)->NewWeakGlobalRef(env, cls_string); return g_weak_cls; }
三茁肠、JNI異常處理
1>JNI層檢查并拋出異常
//JNI 異常處理(java中的try不能捕獲JNI發(fā)生的異常)
JNIEXPORT void JNICALL Java_com_hubin_Jni_1Test_exception
(JNIEnv * env, jobject jobj) {
jclass cls = (*env)->GetObjectClass(env, jobj);
jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
//檢查是否發(fā)送異常
jthrowable ex = (*env)->ExceptionOccurred(env);
// 判斷異常是否發(fā)生
if (ex != NULL) {
jclass newExc;
//清空J(rèn)NI 產(chǎn)生的異常(清空掉異常以后java代碼會繼續(xù)往下執(zhí)行)
(*env)->ExceptionClear(env);
//IllegalArgumentException 創(chuàng)建一個java層可以識別到的異常
newExc = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
if (newExc == NULL){
printf("exception\n");
return;
}
//拋出一個異常
(*env)->ThrowNew(env, newExc, "Throw exception from JNI: GetFieldID faild ");
}
}
2> java層捕獲異常
try{
jni_Test.exception(); //調(diào)用JNI方法的地方
}catch(Exception e){
System.out.println(e.tostring());
}
四患民、緩存策略
-
1.局部靜態(tài)變量進(jìn)行緩存
JNIEXPORT void JNICALL Java_com_hubin_Jni_1Test_cached (JNIEnv * env, jobject jobj) { jclass cls = (*env)->GetObjectClass(env, jobj); //static 存儲在靜態(tài)區(qū) 只需要定義和初始化一次 之后就只需直接使用 生命周期很長 static jfieldID fid = NULL; //靜態(tài)的變量 if (fid == NULL) { fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;"); printf("GetFieldID\n"); } }
-
2.全局變量
jfieldID fid_glb; JNIEXPORT void JNICALL Java_com_hubin_Jni_1Test_cachedGlobal (JNIEnv * env, jclass cls) { if (fid_glb == NULL) { fid_glb = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;"); } }
3.緩存策略和弱引用聯(lián)合使用帶來的問題
1> C中代碼(有問題的代碼模擬)
//局部引用第二次調(diào)用的時候可能已經(jīng)被釋放 會產(chǎn)生野指針
JNIEXPORT jstring JNICALL Java_JniMain_AcessCacheNewString
(JNIEnv * env, jobject jobj) {
//定義一個靜態(tài)的局部變量(盡量避免使用這種情況的使用)
static jclass cls_string = NULL;
if (cls_string == NULL)
{
printf("alvin in Java_JniMain_AcessCache_newString out: \n");
//給局部靜態(tài)變量賦一個局部引用
cls_string = (*env)->FindClass(env, "com/hubin/jni/Test");
}
//使用這個靜態(tài)局部變量
jmethodID jmid = (*env)->GetMethodID(env, cls_string, "getRef", "(I)I");
jthrowable ex = (*env)->ExceptionOccurred(env);
if (ex != NULL)
{
jclass newExc;
// 讓java 繼續(xù)運(yùn)行
(*env)->ExceptionDescribe(env);//輸出關(guān)于這個異常的描述
(*env)->ExceptionClear(env);
printf("C exceptions happend\n");
}
printf("alvin out Java_JniMain_AcessCache_newString\n");
return NULL;
}
JNIEXPORT jstring JNICALL Java_hubin_Jni_1Test_AcessCF
(JNIEnv * env, jobject jobj) {
//Sleep(100);
return Java_JniMain_AcessCacheNewString(env, jobj);
}
2> java中測試代碼
for(int i= 0;i<100;i++){
jni_Test.AcessCF(); //多次調(diào)用jni方法
//大數(shù)組申請大量內(nèi)存,觸發(fā)GC
long[] ar = new long[1024*1024];
}