NDK异常处理
jni异常一般发生在C调用java方法的时候,分为两种情况
- 当我们使用JNIEnv获取jclass、jmethod等出现了类不存在、方法不存在等
- 当我们在native层调用的java方法内部出现异常的时候
异常处理方法
检查异常
我们可以使用ExceptionCheck()方法判断是否出现了异常,如果出现了异常,会返回true。
使用ExceptionClear()则会清除异常,让程序继续执行下去,当然,也可以使用ExceptionDescribe(),这个方法会在日志中打印异常堆栈
jfieldID jfieldId = env->GetFieldID(thiz, "testEx", "I");
if(env->ExceptionCheck()){
LOGD("异常发生了");
env->ExceptionDescribe();
env->ExceptionClear();
}
也可以使用ExceptionOccurred()方法判断,如果出现了异常,则会返回jthrowable对象,否则返回NULL
jthrowable jthrowable1 = env->ExceptionOccurred();
if (jthrowable1) {
env->ExceptionClear();
}
主动抛出异常
我们也可以使用ThrowNew()方法主动抛出异常,参数一是异常的jclass对象
env->ThrowNew(env->FindClass("java/lang/Exception"), "native ex");
注意:ThrowNew()抛出的异常在java层可以try catch到
NDK函数注册
NDK静态注册
通过函数名去匹配对应的native方法,格式如下
extern "C"
JNIEXPORT 返回值类型 JNICALL Java_包名_类名_函数名(JNIEnv * env, jobject obj)
//如下
extern "C"
JNIEXPORT void JNICALL Java_com_example_myapplication_MainActivity_voiceChangeNative
(JNIEnv *env, jobject obj, jint type, jstring path) {
}
缺点:
1、函数名比较长
2、写死了包名+类名,不够灵活
3、运行期才会匹配jni函数,性能低于动态注册
NDK动态注册
动态注册时机
在JNI中,有两个函数,分别是JNI_OnLoad()和JNI_OnUnload(),分别在加载库和卸载库的时候调用,定义在头文件jni.h
中
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);
动态注册代码一般分为两步步
- 使用JavaVM对象获取JNIEnv
- 使用JNIEnv的RegisterNatives方法来注册函数
示例如下
java代码
public class T {
public native static void staticRegisterCall();
public native static int staticRegisterCall(String msg);
}
C代码
const char *class_name = "com/example/myapplication/T";
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
LOGD("JNI_OnLoad");
JNIEnv *env = NULL;
if (jvm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
jclass t_class = env->FindClass(class_name);
//函数指针使用函数名,括号不能丢掉
JNINativeMethod methods[] = {
{"staticRegisterCall", "()V",(void *) (dynamicMethod01)},
{"staticRegisterCall", "(Ljava/lang/String;)I", (int *) (dynamicMethod02)}
};
env->RegisterNatives(t_class,
methods,
sizeof(methods) / sizeof(JNINativeMethod));
return JNI_VERSION_1_6;
}
JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved) {
LOGD("JNI_OnUnload");
}
env->RegisterNatives方法说明
// clazz 代表需要被注册的类
// methods 代表java方法与native方法的对应关系
// nMethods 代表前面methods参数的个数
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,jint nMethods)
JNINativeMethod说明
typedef struct {
const char* name; //java函数的名字
const char* signature; //java函数的签名
void* fnPtr; //native函数指针
} JNINativeMethod;
native方法定义说明
如果native方法不需要JNIEnv、jclass/jobject,那么也可以直接不写。如下所示
// 也OK 如果你用不到 JNIEnv jobject ,可以不用写
void dynamicMethod01() {
LOGD("我是动态注册的函数 dynamicMethod01...");
}
int dynamicMethod02(JNIEnv *env, jclass thiz, jstring valueStr) { // 也OK
const char *text = env->GetStringUTFChars(valueStr, nullptr);
LOGD("我是动态注册的函数 dynamicMethod02... %s", text);
env->ReleaseStringUTFChars(valueStr, text);
return 200;
}