本地方法(JNI)——访问数组元素+错误处理
【0】README
1) 本文文字描述 均轉自 core java volume 2 , 旨在理解 本地方法(JNI)——訪問數組元素+錯誤處理 的基礎知識 ;
2)for source code, please visit : https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter12/chapter12_8
【1】本地方法(JNI)——訪問數組元素
1)元素類型:
- 1.1)Object: Get/SetObjectArrayElement
- 1.2)基本類型: Get/SetXxxArrayElement + ReleaseXxxArrayElements
2) java 編程語言的所有數組類型都有相應的 C 語言類型, 如表12-2所示:
2.1)GetArrayLength 函數: 返回數組的長度;
jarray array = ......; jsize length = (*env)->GetArrayLength(env, array);2.2)怎樣訪問數組元素: 這取決于數組中存儲的是對象還是基本類型的數據
2.3)可以通過 GetObjectArrayElement 和 SetObjectArrayElement 方法: 訪問對象數組的元素;
jobjectArray array = ....; jobject x = (*env)->GetObjectArrayElement(env, array, i); (*env)->SetObjectArrayElement(env, array, j, x); 以上方法效率非常低下;
2.4)GetXxxArrayElement函數: 返回一個指向數組起始元素的C 指針;
2.5)ReleaseXxxArrayElements 函數: 當你不再需要改指針時, 必須記得要調用 ReleaseXxxArrayElements 函數 通知虛擬機;
Attention)
- A1)這里的 Xxx 必須是基本類型,不能是Object;
- A2) 由于指針可能會指向一個 副本, 只有調用相應的 ReleaseXxxArrayElements 函數時, 你所做的改變才能保證在源數組里得到反映;
3)看個荔枝: 下面是對double 類型數組中的所有元素乘以一個常量的示例代碼。 我們獲取一個 java 數組的C 指針a, 并用 a[i] 訪問各個元素;
jdoubleArray array = ...; double scale = ...; double *a = (*env)->GetDoubleArrayElements(env, array_a, NULL); for(i=0; i<(*env)->GetArrayLength(env, array_a); i++)a[i] = a[i] * scale; (*env)->ReleaseDoubleArrayElements(env, array_a, a, 0);4)虛擬機是否確實需要對數組進行拷貝: 這取決于他是如何分配數組和如何進行垃圾回收的。 有些拷貝型的垃圾回收器例行進行移動對象,并更新對象引用;
5)該策略與 將數組鎖定在 特定位置是不兼容的, 因為回收器不能更新本地代碼中的指針值;
6) GetXxxArrayRegion和 SetXxxArrayRegion 函數: 能把一定范圍內的元素從 java 數組復制到 C 數組中或從 C 數組復制到 java 數組中;
7)NewXxxArray 函數: 該函數在本地方法中創建新的 java 數組;
【2】錯誤處理
1)problem+solution:
- 1.1)problem: C的運行期系統對數組越界錯誤, 不良指針造成的間接錯誤等不提供任何防護;而C語言沒有異常;
1.2)solution: 必須調用 Throw 或 ThrowNew 函數來創建一個新的異常對象。 當本地方法退出時, java 虛擬機就會拋出該異常;
2)NewObject 方法: 要使用 Throw函數,就需要使用 NewObject 來創建一個 Throwable 子類的對象。
3)看個荔枝:我們分配了一個EOFException 對象,然后將其拋出:
4)通常調用ThrowNew 會更加方便: 因為只需要提供一個類和一個 “改良UTF-8”字節序列, 該函數就會構建一個異常對象;
> (*env)->ThrowNew(env, (*env)->FindClass(env, "java/io/EOFException"), "Unexpected end of file");5) Throw 和 ThrowNew 都僅僅只是發布異常, 他們不會中斷本地方法的控制流。只有當該方法返回時, java 虛擬機才會拋出異常。所以,每一個對 Throw 和 ThrowNew 的調用語句之后總是緊跟著 return 語句; (干貨——Throw 和 ThrowNew 都僅僅只是發布異常, 他們不會中斷本地方法的控制流。)
6)problem+solution:
- 6.1)problem:通常, 本地代碼不需要考慮捕獲java 異常。 但是,當本地方法調用java 方法時, 該方法可能會拋出異常;
- 6.2)solution:在這類情況下, 本地方法應該調用 ExceptionOccured 方法來確認是否有異常拋出。 如果沒有任何異常被掛起, 則下面的調用返回 NULL, 否則返回一個當前異常對象 的引用;
- 6.3)如果只要檢查是否有異常拋出: 調用, jboolean occured = (*env)->ExceptionCheck(env);
7)本地方法處理異常
7.1)確定異常是否能夠處理,如果能夠處理, 必須調用下面的函數來關閉該異常:
(*env)->ExceptionClear(env);
7.2) 在我們的荔枝中, 我們實現了 fprint 本地方法, 這是基于該方法適合編寫為本地方法的假設而實現的。下面是我們拋出的異常(exceptions):
- e1) 如果格式字符串是NULL, 則拋出 空指針異常;
- e2) 如果格式字符串不含適合打印 double 所需的 % 說明符, 則拋出 IllegalArgumentException異常;
e3)如果調用malloc 失敗, 則拋出 OutOfMemoryException;
- Attention) 本文給出的荔枝只po 了最后C語言拋出異常的結果, 沒有將java 調用本地方法的steps 全部po出來。 因為博主我已經po這個steps , 都po厭煩了,我的本地(JNI)博文中有相應的 steps;for detailed steps , please visit http://blog.csdn.net/pacosonswjtu/article/details/50618022
8) 本地方法的C語言實現(source code at a glance , maybe you should attend for annotations below)
#include "Printf4.h" #include <string.h> #include <stdlib.h> #include <float.h>/**@param format a string containing a printf format specifier(such as "%8.2f"). Substrings "%%" are skipped.@return a pointer to the format specifier (skipping the '%')or NULL if there wasn't a unique format specifier*/ char* find_format(const char format[]) { char* p;char* q;p = strchr(format, '%');while (p != NULL && *(p + 1) == '%') /* skip %% */p = strchr(p + 2, '%');if (p == NULL) return NULL;/* now check that % is unique */p++;q = strchr(p, '%');while (q != NULL && *(q + 1) == '%') /* skip %% */q = strchr(q + 2, '%');if (q != NULL) return NULL; /* % not unique */q = p + strspn(p, " -0+#"); /* skip past flags */q += strspn(q, "0123456789"); /* skip past field width */if (*q == '.') { q++; q += strspn(q, "0123456789"); }/* skip past precision */if (strchr("eEfFgG", *q) == NULL) return NULL;/* not a floating-point format */return p; }JNIEXPORT void JNICALL Java_Printf4_fprint(JNIEnv* env, jclass cl, jobject out, jstring format, jdouble x) { const char* cformat;char* fmt;jclass class_PrintWriter;jmethodID id_print;char* cstr;int width;int i;if (format == NULL) { (*env)->ThrowNew(env, /* ThrowNew 僅僅是發布異常,而不是拋出異常,當函數返回時才會拋出異常 */(*env)->FindClass(env,"java/lang/NullPointerException"),"Printf4.fprint: format is null");return;}/* 創建給定格式的字符串 */cformat = (*env)->GetStringUTFChars(env, format, NULL);fmt = find_format(cformat);if (fmt == NULL){ (*env)->ThrowNew(env, /* ThrowNew 的作用同上 */(*env)->FindClass(env,"java/lang/IllegalArgumentException"),"Printf4.fprint: format is invalid");return;}width = atoi(fmt);if (width == 0) width = DBL_DIG + 10;cstr = (char*)malloc(strlen(cformat) + width);if (cstr == NULL){ (*env)->ThrowNew(env,(*env)->FindClass(env, "java/lang/OutOfMemoryError"),"Printf4.fprint: malloc failed");return;}sprintf(cstr, cformat, x);/* 當你不再需要改指針時, 必須記得要調用 ReleaseXxxArrayElements 函數 通知虛擬機; */(*env)->ReleaseStringUTFChars(env, format, cformat);/* now call ps.print(str) *//* get the class ==獲取類 */class_PrintWriter = (*env)->GetObjectClass(env, out);/* get the method ID == 獲取方法標識 */id_print = (*env)->GetMethodID(env, class_PrintWriter, "print", "(C)V");/* call the method == 通過方法標識調用方法 */for (i = 0; cstr[i] != 0 && !(*env)->ExceptionOccurred(env); i++)(*env)->CallVoidMethod(env, out, id_print, cstr[i]);free(cstr); }總結
以上是生活随笔為你收集整理的本地方法(JNI)——访问数组元素+错误处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lumia930和950(lumia最后
- 下一篇: 本地方法(JNI)——使用调用API