Android JNI开发(1)--JavaVM和 JNIEnv 动态注册本地方法
2017-09-18 18:03
666 查看
这里说的JNI不是初学Android JNI时的那种,而是使用NDK相应的API进行相应的开发工作。
JNIEnv类型是一个指向全部JNI方法的指针。该指针只在创建它的线程有效,不能跨线程传递。多线程无法共享。
使用JNI_OnLoad方法,这个方法需要自己实现。如下
方法名一定不能写错,不然系统不会调用这个方法。这个方法是在加载相应的.so包的时候,系统主动调用的,即:
这句代码调用时,JNI_OnLoad方法就会被调用。
每一个.so文件中,只能包含一个JNI_OnLoad方法,也就是在当前.so包含的文件中,只能有一个.c文件中实现这个JNI_OnLoad方法。
如上面的做法,我们可以在头文件中声明extern JavaVM *savedVm;,然后其他文件include这个头文件后,就可以使用这个JavaVM。
很多情况下,我们需要在其他文件中的一个多线程中使用JNIEnv,比如在线程中使用FindClass方法,这个时候,我们就可以通过JavaVM对象来获取JNIEnv,如下:
AttachCurrentThread表示当前线程与JVM进行关联,这样才能获取到JNIEnv对象;
这样我们就可以在多线程中使用JNIEnv对象,然后使用FindClass方法。当然,使用完了需要解除当前线程和虚拟机的关联,savedVm->DetachCurrentThread();,不然会报异常。
还可以在应用退出时,卸载当前虚拟机,jint DestroyJavaVM(JavaVM* vm);
JNINativeMethod结构体如下:
第一个变量name是Java中函数的名字。
第二个变量signature,用字符串是描述了Java中函数的参数和返回值
第三个变量fnPtr是函数指针,指向native函数。前面都要接 (void *)
第一个变量与第三个变量是对应的,一个是java层方法名,对应着第三个参数的native方法名字
关于第二个变量,表示的参数和返回值表示,可以看如下:
类型签名 对应的Java类型
Z ——-> boolean
B ——-> byte
C ——-> char
S ——-> short
I ——-> int
J ——-> long
F ——-> float
D ——-> double
L全类名;——->类 比如Ljava/lang/String;
[ type ——-> type[] 比如 [B [I
(参数类型签名,…)返回值类型签名 方法类型
可以查看https://www.zybuluo.com/cxm-2016/note/563686
当然,我们也可以在应用退出时卸载本地方法。JNI_OnLoad方法是在动态库被加载时调用,而JNI_OnUnload则是在本地库被卸载时调用。所以这两个函数就是一个本地库最重要的两个生命周期方法。
一、JNI中获取JavaVM和 JNIEnv
JavaVM是虚拟机在JNI中的表示,一个虚拟机中只有一个JavaVM对象,这个对象是线程共享的。JNIEnv类型是一个指向全部JNI方法的指针。该指针只在创建它的线程有效,不能跨线程传递。多线程无法共享。
使用JNI_OnLoad方法,这个方法需要自己实现。如下
jint JNI_OnLoad(JavaVM *vm,void *reserved){ LOGE("JNI_Onload in 1"); JNIEnv *env = NULL; int result = -1; if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) { LOGE("JNI_Onload in 2"); return JNI_ERR; } result = register_method(env);//动态注册本地方法 savedVm = vm;//保存JavaVM ,供其他地方使用。 LOGE("JNI_Onload in 3 , result is %d", result); return JNI_VERSION_1_6; }
方法名一定不能写错,不然系统不会调用这个方法。这个方法是在加载相应的.so包的时候,系统主动调用的,即:
static { System.loadLibrary("hello"); }
这句代码调用时,JNI_OnLoad方法就会被调用。
每一个.so文件中,只能包含一个JNI_OnLoad方法,也就是在当前.so包含的文件中,只能有一个.c文件中实现这个JNI_OnLoad方法。
如上面的做法,我们可以在头文件中声明extern JavaVM *savedVm;,然后其他文件include这个头文件后,就可以使用这个JavaVM。
很多情况下,我们需要在其他文件中的一个多线程中使用JNIEnv,比如在线程中使用FindClass方法,这个时候,我们就可以通过JavaVM对象来获取JNIEnv,如下:
JNIEnv *env ; if (savedVm->AttachCurrentThread(&env, 0) != 0) { LOGI("Failed to attach current thread"); return; }
AttachCurrentThread表示当前线程与JVM进行关联,这样才能获取到JNIEnv对象;
这样我们就可以在多线程中使用JNIEnv对象,然后使用FindClass方法。当然,使用完了需要解除当前线程和虚拟机的关联,savedVm->DetachCurrentThread();,不然会报异常。
还可以在应用退出时,卸载当前虚拟机,jint DestroyJavaVM(JavaVM* vm);
二、动态注册JNI方法
动态注册JNI方法的时机也是在JNI_OnLoad方法中:jint JNI_OnLoad(JavaVM *vm,void *reserved){ JNIEnv *env = NULL; int result = -1; if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) { LOGE("JNI_Onload in 2"); return JNI_ERR; } **result = register_method(env);//动态注册本地方法** savedVm = vm;//保存JavaVM ,供其他地方使用。 LOGE("JNI_Onload in 3 , result is %d", result); return JNI_VERSION_1_6; } //这是本地方法的一个集合 JNINativeMethod methods[] = { {"helloMethod","(Ljava/lang/String;)Ljava/lang/String;",(void*)helloMethod}, {"nativeCallJava","()V",(void*)nativeCallJava}, {"nativeCallJavaInThread","()V",(void*)nativeCallJavaInThread} }; jint register_method(JNIEnv *env){ //开始注册方法 int result = registerNativeMethods(env,"com/xxxx/jni/MainActivity",methods, sizeof(methods) / sizeof(methods[0])); return result; } jint registerNativeMethods(JNIEnv* env, const char *class_name, JNINativeMethod *methods, int num_methods) { int result = 0; //先根据类名找到类,这个类一般是用来存放所有的native方法 jclass clazz = env->FindClass(class_name); if(clazz == NULL){ return JNI_FALSE; } //调用JNI方法,注册方法 result = env->RegisterNatives(clazz, methods, num_methods); if(result < 0){ return JNI_FALSE; } return result; }
JNINativeMethod结构体如下:
typedef struct { const char* name; const char* signature; void* fnPtr; } JNINativeMethod;
第一个变量name是Java中函数的名字。
第二个变量signature,用字符串是描述了Java中函数的参数和返回值
第三个变量fnPtr是函数指针,指向native函数。前面都要接 (void *)
第一个变量与第三个变量是对应的,一个是java层方法名,对应着第三个参数的native方法名字
关于第二个变量,表示的参数和返回值表示,可以看如下:
类型签名 对应的Java类型
Z ——-> boolean
B ——-> byte
C ——-> char
S ——-> short
I ——-> int
J ——-> long
F ——-> float
D ——-> double
L全类名;——->类 比如Ljava/lang/String;
[ type ——-> type[] 比如 [B [I
(参数类型签名,…)返回值类型签名 方法类型
可以查看https://www.zybuluo.com/cxm-2016/note/563686
当然,我们也可以在应用退出时卸载本地方法。JNI_OnLoad方法是在动态库被加载时调用,而JNI_OnUnload则是在本地库被卸载时调用。所以这两个函数就是一个本地库最重要的两个生命周期方法。
int unRegisterNative(JNIEnv *env) { jclass clazz = env->FindClass(“类名”); if (clazz == NULL) { return false; } return env->UnregisterNatives(clazz) ; } void JNI_OnUnload(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; jint result = -1; if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { return; } int ret = unRegisterNative(env); }
相关文章推荐
- JavaSE JNI 动态注册本地方法(c语言实现native层)
- JavaSE JNI 动态注册本地方法(c语言实现native层)
- android开发--- jni使用RegisterNatives注册本地方法
- Android-本地方法C调用Java中的方法/NDK-JNI开发实例(六)
- Android Studio JNI 的静动态注册开发以及C/C++ JNIEnv的理解
- Android JNI开发高级篇有关Android JNI开发中比较强大和有用的功能就是从JNI层创建、构造Java的类或执行Java层的方法获取属性等操作。 一、类的相关操作 1. jclass FindClass(JNIEnv *env, const char *name);
- Android Studio Jni开发(三)Native方法动态注册
- Android-java调用本地方法返回字符串显示在界面上/NDK-JNI开发实例(二)
- Android-本地方法与Java相互调用-自定义ProgressBar(锅炉压力监测例子)/NDK-JNI开发实例(七)
- JavaSE JNI 动态注册本地方法(c语言实现native层)
- Android Studio NDK 入门教程(8)--JNI动态注册本地方法
- 安卓 jni 开发之 native 方法的动态注册
- Android JNI开发之c语言调用java方法
- Android JNI使用方法(“动态注册”)
- Android-调用本地方法实现将C进程分支出来即生成系统进程/NDK-JNI开发实例(九)
- android jni 的编写二 (NDK 开发中动态注册Jni)
- [Android]JNI动态注册Java函数教程
- Android-调用本地方法将小写字符串转成大写字符串/NDK-JNI开发实例(四)
- Android-使用C++实现调用本地方法返回字符串显示在界面上/NDK-JNI开发实例(八)
- Android JNI动态注册Native 方法(实现IDA中改名)