前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JNI动态注册以及JNI签名

JNI动态注册以及JNI签名

作者头像
故乡的樱花开了
发布2023-10-22 14:09:30
3090
发布2023-10-22 14:09:30
举报
文章被收录于专栏:Android技术专栏Android技术专栏

一.动态注册和静态注册

  注册native方法有两种方式,动态注册和静态注册。静态注册是在编译时进行注册,而且在java中声明的native方法和c/c++中的本地方法的对应关系是恒定的;比如说在com.example.test包下的Test类中声明了一个stringFromJNI()的native方法,那么它对应的c/c++中的本地方法名就是Java_com_example_test_Test_stringFromJNI();并且这个方法名不能做任何的修改,在java中调用stringFromJNI()函数时,就会按包名_类名_方法名的形式找到对应的方法并调用。而动态注册是在运行时进行注册的,而且本地方法的名字可以按自己的喜好随意取,只要说明了java中声明的native方法和c/c++中的本地方法的对应关系即可。下面用代码的形式来演示一下动态注册的使用步骤。

二.动态注册的步骤

  1.在java中声明native方法,并在静态代码块中加载动态库:

代码语言:javascript
复制
public class Test {
    static{
        System.loadLibrary("dynamic"); //加载动态库
    }
  //声明native方法
    native String stringFromJNI();
    
    native static int add(int a,int b);
}

  2.注册函数:在java中加载动态库的时候,虚拟机会调用JNI库中的JNI_Onload()函数,动态注册就是在这个函数中进行的。动态注册使用的是RegisterNatives()方法,这个方法接收3个参数,分别是:

    1.jclass clazz  声明native方法的java类

    2.const JNINativeMethod* methods  JNINativeMethod类型的结构体数组,我们就是在这个结构体数组中说明java方法和本地方法的对应关系的

    3.jint nMethods  第二个参数methods所指向的结构体数组的大小

    JNINativeMethod结构体的定义如下:

代码语言:javascript
复制
    typedef struct {
        const char* name;//java中的方法名
        const char* signature;//jni签名
        void*       fnPtr;//本地函数的指针
    } JNINativeMethod;

    下面给出这部分的代码:

代码语言:javascript
复制
#include <jni.h>
#include <string>
using namespace std;

/**
*实现本地方法,名字可以任取,方法的前两个参数是固定的,后面的参数就是实际的参数
*/

jstring native_stringFromJNI(JNIEnv *env,jobject thiz){
    return env->NewStringUTF("nihao");
}
jint native_add(JNIEnv *env,jobject thiz,jint a,jint b){
    return a+b;
}

static const JNINativeMethod nativeMethod[]={
        {"stringFromJNI","()Ljava/lang/String;", (void *) native_stringFromJNI},
        {"add","(II)I",(void *)native_add}
};

static int registerNativeMethod(JNIEnv *env){
    int result=-1;
    jclass clazz=env->FindClass("com/example/dynamicregister/Test");
    if(env->RegisterNatives(clazz,nativeMethod,sizeof(nativeMethod)/sizeof(nativeMethod[0]))==JNI_OK){
        result=0;
    }
    return result;
}

jint JNI_OnLoad(JavaVM *vm,void *reserved){//这个方法是一个override方法,在加载动态库时,会自动调用,一般用来做一些初始化操作,动态注册的代码就可以写在这
    JNIEnv *env= nullptr;
    if(vm->GetEnv((void **)&env,JNI_VERSION_1_4)==JNI_OK){//首先需要获取JNIEnv *env指针,因为registerNativeMethod方法会用到
        if(registerNativeMethod(env)==0){
            return JNI_VERSION_1_4; //返回值代表动态库需要的jni版本
        }
    }
    return -1;
}

  3.在java中调用native函数:

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {
    private TextView tv_display;

    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv_display=findViewById(R.id.tv_display);
        tv_display.setText(Test.add(1,10)+"");
    }
}

三.JNI签名

  下面我把和签名有关的代码单独拿出来进行说明:

代码语言:javascript
复制
static const JNINativeMethod nativeMethod[]={
        {"stringFromJNI","()Ljava/lang/String;", (void *) native_stringFromJNI},
        {"add","(II)I",(void *)native_add}
};

  比如第一个函数的签名:()Ljava/lang/String;其实表示的是java中的stringFromJNI函数的形参为空,返回值类型为String。

  第二个函数的签名(II)I表示java中的add函数的形参列表是(int,int),返回值类型也是int类型。

  那这个签名有什么作用呢?其实是为了解决java中的函数重载问题。比如,如果java中还声明了一个方法,native String stringFromJNI(String str);那么如果没有函数签名的话,就不知道c/c++中的native_stringFromJNI()对应的是java中的哪个stringFromJNI函数。

  接下来,通过一个表格来说明JNI基本类型的签名以及引用类型的签名:

   特别需要注意的是,引用类型签名后面的分号;一定不能省略,否则编译通过不了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-09-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档