JNI-静态注册和动态注册

时间:2021-7-3 作者:qvyue

静态注册

默认情况下,就是静态注册,静态注册是最简单的方式,NDK开发过程中,基本上使用静态注册。

新建一个Java工程。添加运行测试Java文件

JNI-静态注册和动态注册

image

publicclassTestDemo{publicnativeStringstringFromJNI();// 静态注册publicnativevoidstaticRegister();// 静态注册publicstaticvoidmain(String[]args){TestDemodemo=newTestDemo();System.out.println(demo.stringFromJNI());}}

使用javah自动生成JNI C函数头文件

$ javah com.jni.dynamic.register.TestDemo

JNI-静态注册和动态注册

image

/* DO NOT EDIT THIS FILE – it is machine generated */#include/* Header for class com_jni_dynamic_register_TestDemo */#ifndef_Included_com_jni_dynamic_register_TestDemo#define_Included_com_jni_dynamic_register_TestDemo#ifdef__cplusplusextern”C”{#endif/*

* Class:    com_jni_dynamic_register_TestDemo

* Method:    stringFromJNI

* Signature: ()Ljava/lang/String;

*/JNIEXPORT jstring JNICALL Java_com_jni_dynamic_register_TestDemo_stringFromJNI(JNIEnv*,jobject);/*

* Class:    com_jni_dynamic_register_TestDemo

* Method:    staticRegister

* Signature: ()V

*/JNIEXPORTvoidJNICALL Java_com_jni_dynamic_register_TestDemo_staticRegister(JNIEnv*,jobject);#ifdef__cplusplus}#endif#endif

JNIEXPORT jstring JNICALL Java_com_jni_dynamic_register_TestDemo_stringFromJNI(JNIEnv *, jobject);和JNIEXPORT void JNICALL Java_com_jni_dynamic_register_TestDemo_staticRegister (JNIEnv *, jobject);

这种通过javah默认生成C函数方法声明的方式就是静态注册

通过Clion新建C++工程,com_jni_dynamic_register_TestDemo.h放入工程中

JNI-静态注册和动态注册

image

#include”com_jni_dynamic_register_TestDemo.h”#include#includeusingnamespacestd;JNIEXPORT jstring JNICALL Java_com_jni_dynamic_register_TestDemo_stringFromJNI(JNIEnv*env,jobject thiz){std::string hello=”默认就是静态注册哦”;coutNewStringUTF(hello.c_str());}JNIEXPORTvoidJNICALL Java_com_jni_dynamic_register_TestDemo_staticRegister(JNIEnv*env,jobject thiz){cout

生成动态库

add_library(dynamicRegister SHARED dynamic_register.cpp)

JNI-静态注册和动态注册

image

个人使用的macOS 所以动态库是.dylib

“静态注册”方法确实帮我们省了很多事情,但是也有相应的缺点

首次调用 Java 的 native 方法,虚拟机会去搜寻对应的 Native 层的函数,这就有点影响执行效率了。如果搜索到了,就会建立映射关系,下次就不用再浪费时间去搜索了( 运行期 才会去 匹配JNI函数,性能上 低于 动态注册)。

Native 层的函数名字太长,名字的格式为 Java_包名_类名_方法名,例如Java_com_jni_dynamic_register_TestDemo_staticRegister。

有”静态注册”,当然就有”动态注册”,那么相比较而言,有哪些优缺点呢

“动态注册”需要我们手动建立函数映射关系,虽然增加了代码量,但是可以提供运行效率。

“动态注册”允许我们自定义函数名字。

相比于”静态注册”,工作效率高。

虽然”动态注册”相比于”静态注册”有这么多好处,但是需要一定的学习成本,但是这也是非常值得的,那么我们就开始吧。

加载动态库

我们知道,在 Java 层通过 System.loadLibrary() 方法可以加载一个动态库,此时虚拟机就会调用JNI库中的 JNI_OnLoad() 函数(放在哪个文件无所谓),函数原型如下

#includejintJNI_OnLoad(JavaVM*vm,void*reserved);

参数介绍

vm: JavaVM 指针

reserved: 类型为 void *,这个参数是为了保留位置,以供将来使用。

返回值代表被动态库需要的JNI版本,当然,如果虚拟机无法识别这个返回的版本,那么动态库也加载不了。

目前已有的返回值有四个,分别为 JNI_VERSION_1_1, JNI_VERSION_1_2, JNI_VERSION_1_4, JNI_VERSION_1_6。

如果动态库没有提供 JNI_OnLoad() 函数,虚拟机会假设动态库只需要 JNI_VERSION_1_1 版本即可,然而这个版本太旧,很多新的函数都没有,因此我们最好在动态库中提供这个函数,并返回比较新的版本,例如 JNI_VERSION_1_6。

//====================dynamic_register.cpp==================#include”com_jni_dynamic_register_TestDemo.h”#include#includeusingnamespacestd;JNIEXPORT jstring JNICALL Java_com_jni_dynamic_register_TestDemo_stringFromJNI(JNIEnv*env,jobject thiz){std::string hello=”默认就是静态注册哦”;coutNewStringUTF(hello.c_str());}JNIEXPORTvoidJNICALL Java_com_jni_dynamic_register_TestDemo_staticRegister(JNIEnv*env,jobject thiz){cout

JNI-静态注册和动态注册

image

注册函数

JNI_OnLoad() 函数经常会用来做一些初始化操作,”动态注册”就是在这里进行的。

“动态注册”可以通过调用_JNIEnv结构体的 RegisterNatives() 函数

struct_JNIEnv{conststructJNINativeInterface*functions;#ifdefined(__cplusplus)jintRegisterNatives(jclass clazz,constJNINativeMethod*methods,jint nMethods){returnfunctions->RegisterNatives(this,clazz,methods,nMethods);}#endif/*__cplusplus*/}

实际使用的函数原型如下

jintRegisterNatives(JNIEnv*env,jclass clazz,constJNINativeMethod*methods,jint nMethods);

参数解释

env: JNIEnv指针.

clazz: 代表 Java 的一个类。

methos: 代表结构体 JNINativeMethod 数组。JNINativeMethod结构体定了Java层的native方法和底层的函数的映射关系。

nMethods: 代表第三个参数methods所指向的数组的大小。

返回值

0代表成功,负值代表失败。

JNINativeMethod结构体

RegisterNatives() 函数最重要的部分就是 JNINativeMethod 这个结构体,我们看下这个结构体声明

typedefstruct{constchar*name;constchar*signature;void*fnPtr;}JNINativeMethod;

name表示Java的native方法的名字。

signature表示方法的签名。

fnPtr是一个函数指针,指向JNI层的一个函数,也就是和Java层的native建立映射关系的函数。

实现动态注册

有了前面的所有基础,那么我们就可以实现自己的动态注册功能了。

首先带有native方法的Java类如下

publicclassTestDemo{static{System.load(“/Volumes/CodeApp/SourceWork/CPlus_workspace/JNI-Demo-Lib/cmake-build-debug/dynamic_register/libdynamicRegister.dylib”);}publicnativeStringstringFromJNI();// 静态注册publicnativevoidstaticRegister();// 静态注册publicnativevoiddynamicJavaMethod01();// 动态注册1publicnativeintdynamicJavaMethod02(StringvalueStr);// 动态注册2publicstaticvoidmain(String[]args){}}

根据头文件的注释和函数原型,我们就可以实现如下的动态注册

// native 真正的函数// void dynamicMethod01(JNIEnv *env, jobject thiz) { // OK的voiddynamicMethod01(){// 也OK  如果你用不到  JNIEnv jobject ,可以不用写coutGetStringUTFChars(valueStr,nullptr);coutReleaseStringUTFChars(valueStr,text);return200;}/*

typedef struct {

    const char* name;      // 函数名

    const char* signature; // 函数的签名

    void*      fnPtr;    // 函数指针

} JNINativeMethod;

*/staticconstJNINativeMethod jniNativeMethod[]={{“dynamicJavaMethod01″,”()V”,(void*)(dynamicMethod01)},{“dynamicJavaMethod02″,”(Ljava/lang/String;)I”,(int*)(dynamicMethod02)},};constchar*mainClassName=”com/jni/dynamic/register/TestDemo”;// JNI JNI_OnLoad函数,如果你不写JNI_OnLoad,默认就有JNI_OnLoad,如果你写JNI_OnLoad函数 覆写默认的JNI_OnLoad函数extern”C”JNIEXPORT jintJNI_OnLoad(JavaVM*javaVm,void*){JNIEnv*jniEnv=nullptr;intresult=javaVm->GetEnv(reinterpret_cast(&jniEnv),JNI_VERSION_1_6);// result 等于0  就是成功    if(result!=JNI_OK){return-1;// 会奔溃}coutFindClass(mainClassName);// jint RegisterNatives(Class, 我们的数组==jniNativeMethod, 注册的数量 = 2)jniEnv->RegisterNatives(mainClass,jniNativeMethod,sizeof(jniNativeMethod)/sizeof(JNINativeMethod));cout

publicstaticvoidmain(String[]args){System.out.println(“——————-start main——————-“);TestDemodemo=newTestDemo();demo.dynamicJavaMethod01();System.out.println(“——————-华丽的分割线——————-“);demo.dynamicJavaMethod02(“hello 2222”);}

RUN>

System.loadLibrary—》JNILoad init动态 注册没有毛病——————-start main——————-我是动态注册的函数 dynamicMethod01…——————-华丽的分割线——————-我是动态注册的函数 dynamicMethod02…hello2222

JNI-静态注册和动态注册

image

总结

“动态注册”功能还是比较简单的,只需要搞清楚JNI_OnLoad()和RegisterNatives()函数的使用就行。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。