Android C++系列:JNI调用时的异常处理

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Android JNI 调用时的异常主要有如下两种:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Native 代码调用 Java 层代码时发生了异常要处理","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Native 代码自己抛出了一个异常让 Java 层去处理 可以看到异常的发生和处理基本都需要 Native 和 Java 交互,而对于 Native 自身出了异常,也就是 C/C++ 代码有问题,导致应用崩溃的又是另一回事了。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Native 调用 Java 方法时的异常","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之前讲述了如何从 Native 调用 Java 的方法,先准备一个有异常的方法供 Native 去调用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"private int exceptionFun() { return 2 / 0; } \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后在 Native 中调用该方法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"jclass cls = env->FindClass(\"com/qingkouwei/Demo\");\n jmethodID mid = env->GetMethodID(cls, \"\", \"()V\");\n jobject obj = env->NewObject(cls, mid);\n mid = env->GetMethodID(cls, \"exceptionFun\", \"()I\");\n // 先初始化一个类,然后调用类方法,就如文章中描述的那样\n env->CallIntMethod(obj, mid);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除数为 0 ,一调用应用直接崩溃了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" java.lang.ArithmeticException: divide by zero \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来就是要进行异常处理的部分了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"jclass cls = env->FindClass(\"com/qingkouwei/Demo\");\n jmethodID mid = env->GetMethodID(cls, \"\", \"()V\");\n jobject obj = env->NewObject(cls, mid);\n mid = env->GetMethodID(cls, \"exceptionFun\", \"()I\");\n\n // 先初始化一个类,然后调用类方法,就如博客中描述的那样\n env->CallIntMethod(obj, mid);\n //检查是否发生了异常\n jthrowable exc = env->ExceptionOccurred();\n // jboolean result = env->ExceptionCheck();\n\n if (exc) {\n // 打印异常日志\n env->ExceptionDescribe();\n // 这行代码才是关键不让应用崩溃的代码,\n env->ExceptionClear();\n // 发生异常了要记得释放资源\n env->DeleteLocalRef(cls);\n env->DeleteLocalRef(obj);\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"添加如上代码进行处理后,应用并不会直接崩溃了,并且在 LogCat 中会看到对应的异常日志,这里面到了做了哪些操作呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Native 提供了 ExceptionOccurred 和 ExceptionCheck 方法来检测是否有异常发生,前者返回的是 jthrowable 类型,后者返回的是 jboolean 类型。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果有异常,会通过 ExceptionDescribe 方法来打印异常信息,方便我们在 LogCat 中看到对应的信息。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而 ExceptionClear 方法则是关键的不会让应用直接崩溃的方法,类似于 Java 的 catch 捕获异常处理,它会消除这次异常。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这样就把由 Native 调用 Java 时的一个异常进行了处理,当处理完异常之后,别忘了释放对应的资源。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不过,我们这样仅仅是消除了这次异常,还应该让调用者有异常的发生,那么就需要通过 Native 来抛出一个异常告诉 Java 调用者了。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Native 抛出 Java 中的异常","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有时在 Native 代码中进行一些操作,需要抛出异常到 Java ,交由上层去处理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如 Java 调用 Native 方法传递了某个参数,而这个参数有问题,那么 Native 就可以抛出异常让 Java 去处理这个参数异常的问题。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Native 抛出异常的代码大致都是相同的,可以抽出一个通用函数来:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"void throwByName(JNIEnv *env, const char *name, const char *msg) {\n jclass cls = env->FindClass(name);\n if (cls != NULL) {\n env->ThrowNew(cls, msg);\n }\n env->DeleteLocalRef(cls);\n }\n // 调用抛出异常\n extern \"C\"\n JNIEXPORT void JNICALL\n Java_com_qingkouwei_Demo_nativeThrowException(JNIEnv *env, jobject instance) {\n throwByName(env, \"java/lang/IllegalArgumentException\", \"native throw exception\");\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根据异常类型和异常信息来抛出异常。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而在 Java 中,就必须要用 try … catch… 来准备好捕获这次异常了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"try {\n nativeThrowException();\n } catch (IllegalArgumentException e) {\n Log.e(\"NativeMethod\", e.getMessage());\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"小结","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了以上两种异常情况之外,还有一个特别常见的异常,就是当判断某个变量为 NULL 之后,执行立即返回的操作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当发生异常时,一定要先处理异常,然后才能继续执行后面的步骤。如果不是需要立即返回的,那么就通过 ExceptionClear清除这次异常,然后在进行其他的处理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于在 Native 中发生了异常,需要让 Java 层去处理了,则在 Native 中抛出对应的异常,由 Java 层去捕获,比如在使用 ExceptionClear 清除了异常之后,就可以通过 throwNew 来抛出异常信息。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"具体的异常处理方法和时机还是要看具体的使用场景,选择最合适的处理方法。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章