浅析Java中JNI静态注册和动态注册
作者:louisgeek
这篇文章主要为大家详细介绍了Java中JNI静态注册和动态注册的相关知识,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下
静态注册是指 Java native 方法和对应的本地函数按照预先定义好的命名规则来建立关联,实现了 Java native 方法与本地函数实现的绑定,这些函数会在 Java 类加载时被静态注册到 JVM 中,以确保 JVM 能够正确地找到对应的函数
动态注册是通过提供一个 Java native 方法和本地函数一一对应的映射表,然后在 JNI_OnLoad 回调里将映射表注册给 JVM,这样一来 JVM 就可以用这个映射表来调用相应的函数了,而不必像静态注册那样通过函数名去查找需要调用的函数,所以本地函数名不需要遵循特定命名规则
PS:在 JNI 中静态注册和静态库之间没有直接的关系,无论是静态注册还是动态注册都可以生成动态库(.so文件)或者静态库(.a 文件)
public class MainActivity extends AppCompatActivity { // Used to load the 'myjni' library on application startup. static { System.loadLibrary("myjni"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // String str = stringFromJNI(); Log.e("TAG", "onCreate: str="+str); } /** * A native method that is implemented by the 'myjni' native library, * which is packaged with this application. */ public native String stringFromJNI(); //java native 方法 }
静态注册
本地函数的函数名需要遵循 Java_类全限定名_方法名 的格式(类全限定名中的 . 需要用 _ 替换)命名规范
extern "C" JNIEXPORT jstring JNICALL Java_com_louis_myjni_MainActivity_stringFromJNI( JNIEnv* env, //JNIEnv 指的是当前 JNI 环境,利用 JNIEnv 可以操作 java 层代码 jobject /* this */) { //jobject 指的是 JNI 函数对应的 java native 方法所在类的实例对象,如果 native 方法是 static 的话则类型就需要改成 jclass,代表的是类对象 std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
动态注册
- 当 Java 层调用 System.loadLibrary 方法时,JNI_OnLoad 函数会在库被加载到 Java 虚拟机时调用
- PS:比如 Android 系统的 JNI 函数和 FFmpeg 相关函数和都是使用的动态注册
//定义本地函数,函数名任意 jstring stringFromJNI_JNI_OnLoad(JNIEnv *env, jobject/* this */) { std::string hello = "Hello from C++ stringFromJNI_JNI_OnLoad"; return env->NewStringUTF(hello.c_str()); } //定义一个 JNINativeMethod 结构体数组(映射表),结构体包含 Java 方法名、方法签名(方法描述符)以及对应关联的本地函数的指针 JNINativeMethod nativeMethods[] = { //stringFromJNI 对应 Java native 方法,stringFromJNI_JNI_OnLoad 对应本地函数 {"stringFromJNI", "()Ljava/lang/String;", (jstring *) stringFromJNI_JNI_OnLoad} }; //Java 调用 System.loadLibrary 时此 JNI_OnLoad 函数就会执行 //JavaVM *vm 参数是一个指向 Java 虚拟机的指针,通过这个指针可以访问 JVM 的功能 //void *reserved 是一个保留参数,通常不使用 JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env; //获取与当前线程关联的 JNI 环境 if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { LOGE("获取 JNI 环境失败"); return JNI_ERR; } //通过类的全限定名来查找并加载一个类,返回一个指向该类的 jclass 引用 jclass clazz = env->FindClass("com/louis/myjni/MainActivity"); if (clazz == NULL) { LOGE("获取 jclass 失败"); return JNI_ERR; } //计算 arr 数组的长度 int length = sizeof(arr) / sizeof(arr[0]); int nMethods = sizeof(nativeMethods) / sizeof(nativeMethods[0]); //参数 nMethods 指定数组中 NativeMethod 的数量 if (env->RegisterNatives(clazz, nativeMethods, nMethods) != JNI_OK) { LOGE("注册本地函数失败"); return JNI_ERR; } LOGE("注册成功"); return JNI_VERSION_1_6; //返回当前支持的 JNI 版本号 }
总结
静态注册简单易用,函数必须要有类似 JNIEXPORT jstring JNICALL 这样的相关声明,而且函数名必须要按照 JNI 的命名规则去命名,由于 JNI 函数名暴露,所以存在一定的安全隐患
动态注册灵活性高,不依赖于固定的命名规则,不过需要自行注册手动关联,可以做到在运行时根据条件选择不同的实现,更适合复杂项目
到此这篇关于浅析Java中JNI静态注册和动态注册的文章就介绍到这了,更多相关Java JNI静态注册和动态注册内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!