本地方法(JNI)——访问数组元素+错误处理
2016-02-02 10:05
633 查看
【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 对象,然后将其抛出:
jclass class_EOFException = (*env)->FindClass(env, "java/io/EOFException"); //获取类; jmethodID id_EOFException = (*env)->GetMethodID(env, class_EOFException , "<init>", "()V"); //获取方法标识符; jthrowable obj_exc = (*env)->NewObject(env, class_EOFException ,id_EOFException); // 创建一个 Throwable 子类对象; (*env)->Throw(env, obj_exc); //抛出异常;
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, 否则返回一个当前异常对象 的引用;
jthrowable obj_exc = *(env)->ExceptionOccurred(env);
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); }
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序