JNI异常处理

来源:互联网 发布:mac快捷键大全示意图 编辑:程序博客网 时间:2024/06/05 06:34

JNI异常处理

JNI函数在执行过程中会出现异常,其异常处理机制与Java和C++都不一样。JNI提供了两种检查异常的方法:

方法1 检查上一次 JNI函数调用的返回值是否为NULL。

方法2 通过调用JNI函数ExceptionOccurred()来判断是否发生异常。

检查到异常后必须予以处理。处理异常的方法也有两种:

Native方法可选择立即返回,这样异常就会在调用该Native方法的Java 代码中抛出。所以在Java代码中必须有捕获相应异常的代码,否则程序直接退出。

Native方法可以调用ExceptionClear()来清除异常,然后执行自己的异常处理代码。

JNI提供的检查和处理异常的函数如表2-5所示。

表2-5 JNI异常处理函数

注意 异常出现后,Native相关代码必须先检查清除异常,然后才能进行其他的 JNI 函数调用。当有异常未被清除时,只有以下JNI异常处理函数可被安全地调用:ExceptionOccurred()、ExceptionDescribe()?、ExceptionClear()、ExceptionDescribe()。

接下来,继续以Log系统为例讲解JNI的异常处理流程。先看以下代码:

  1. static jboolean android_util_Log_isLoggable(JNIEnv* env,  
  2.    jobject clazz, jstring tag, jint level)  
  3. {  
  4.    ……  
  5.    if (tag == NULL) {//异常流程,直接退出  
  6.  
  7.   return false;  
  8.    }  
  9.    jboolean result = false;  
  10.    const char* chars = env->GetStringUTFChars(tag, NULL);  
  11.    if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {  
  12.   ……  
  13.     // 异常流程,释放资源,抛出异常  
  14.     env->ReleaseStringUTFChars(tag, chars);  
  15.     jniThrowException(env, "java/lang/IllegalArgumentException", buf2);  
  16.     return false;  
  17. } else {//正常流程  
  18.     result = isLoggable(chars, level);  
  19. }  
  20. env->ReleaseStringUTFChars(tag, chars);  
  21. return result;  
  22. }  

从代码中并没有看到JNI异常处理函数的调用。我们接着分析jniThrowException方法。

该方法定义在JNIHelp.h中,并在JNIHelp.cpp中实现。代码如下:

  1. extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg)  
  2. {  
  3. JNIEnv* e = reinterpret_cast<JNIEnv*>(env);  
  4. if ((*env)->ExceptionCheck(e)) {//判断发生异常  
  5.    scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));  
  6.    (*env)->ExceptionClear(e);//得到异常引用并清除异常  
  7.    if (exception.get() != NULL) {  
  8.       char* text = getExceptionSummary(env, exception.get());  
  9.       free(text);  
  10.     }  
  11.  
  12. }  

原来JNIHelp.h中定义了jniThrowException,它只是把异常处理函数做了一个封装,方便使用而已。

2.7.2 JNI层代码和异常处理(2)

本章开头Log系统的例子就是采用函数注册的方法。这是不是意味着在应用层就不能使用这种方法?答案显然是否定的。接下来把AppJni改造成函数注册。只需要借鉴Log系统JNI注册流程,在原有的app_jni中添加如下代码即可:

  1. #include <string.h> 
  2. #include <jni.h> //引入jni.h头文件。这里定义了所有JNI函数和数据类型  
  3. //这里已经可以不用遵守JNI的函数命名规则,因为我们已经做了函数映射  
  4. Jstring Java_com_allongriver_jni_AppJniActivity_show( JNIEnv* env,jobject thiz )  
  5. {  
  6. // 这部分代码不需要任何改变  
  7. ……  
  8. }  
  9. //下面都是为了完成函数注册添加的代码。这里是Java层方法和JNI层方法的映射  
  10. static JNINativeMethod gmethods[] = {  
  11.    {"show", "()Ljava/lang/String;”,  
  12. (void*)Java_com_allongriver_jni_AppJniActivity_show},  
  13. };  
  14. /*  
  15.  * Register several native methods for one class.  
  16.  */  
  17. static int registerNativeMethods(JNIEnv* env, const char* className,  
  18.        JNINativeMethod* gMethods, int numMethods)  
  19. {  
  20. jclass clazz;  
  21. clazz = (*env)->FindClass(env,className);  
  22. if (clazz == NULL) {  
  23.    return JNI_FALSE;  
  24. }  
  25. //调用JNIEnv提供的注册函数向虚拟机注册  
  26. if ((*env)->RegisterNatives(env,clazz, gMethods, numMethods) < 0) {  
  27.    return JNI_FALSE;  
  28. }  
  29. return JNI_TRUE;  
  30. }  
  31. /*  
  32.  * Register native methods for all classes we know about.  
  33.  * returns JNI_TRUE on success.  
  34.  */  
  35. static int registerNatives(JNIEnv* env)  
  36. {  
  37. if (!registerNativeMethods(env, "com/allongriver/jni/AppJniActivity",  
  38. methods, sizeof(methods) / sizeof(methods[0]))) {  
  39. return JNI_FALSE;  
  40. }  
  41. return JNI_TRUE;  
  42. }  
  43. /*虚拟机执行System.loadLibrary("app_jni")后,进入libapp_jni.so后  
  44.  *会首先执行这个方法,所以我们在这里做注册的动作*/  
  45. jint JNI_OnLoad(JavaVM* vm, void* reserved)  
  46. {  
  47. jint result = -1;  
  48. JNIEnv* env = NULL;  
  49. if ( (*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_4) )  {  
  50.    goto fail;  
  51. }  
  52. //最终调用(*env)->RegisterNatives,这跟Log系统是一样的  
  53. if (registerNatives(env) != JNI_TRUE) {  
  54.    goto fail;  
  55. }  
  56.    result = JNI_VERSION_1_4;  
  57. fail:  
  58.    return result;  

至此读者可能会问,为什么同样是以函数注册方式调用JNI,在Log系统中没有执行System.loadLibrary, 也没有在JNI_OnLoad中执行注册函数呢?

那是因为系统在启动的过程中已经帮我们做了。Android启动篇会介绍这部分内容。如果应用框架层某些模块不是在系统启动过程中自动load并注册,也需要上述JNI_OnLoad步骤。读者可以参考android_media_MediaPlayer.cpp的例子。


0 0
原创粉丝点击