JNI学习(二)

静态注册

接着上篇博客学习

JNI函数

 JNIEXPORT void JNICALL Java_com_example_jnidemo_TextDemo_setText(JNIEnv *env, jobject this, jstring string){
__android_log_print(ANDROID_LOG_ERROR, "test", "invoke set from C\n");char* str = (char*)(*env)->GetStringUTFChars(env,string,NULL);__android_log_print(ANDROID_LOG_ERROR, "test", "%s\n", str);(*env)->ReleaseStringUTFChars(env, string, str);}

JNI函数有两个关键字JNIEXPORT和JNICALL,这两个关键字是宏定义,主要用于说明该函数时JNI函数,在虚拟机加载so库时,如果发现函数含有上面两个宏定义时,就会链接到对应java层的native方法。
"extern c"是避免编译器按照C++的方式去编译C函数,原因是C不支持函数的重载,编译之后函数名不变。C++支持函数的重载(这点与java一致),编译之后函数名会改变

函数名

看到.c中的函数名字是"Java_com_example_jnidemo_TextDemo_setText"而在.java中定义的native函数名:setText,为什么对应到.c中函数名会变成这么长呢?
这跟JNI native函数的注册方式有关
JNI native函数有两种注册方式:
1、静态注册:按照JNI接口规范的命名规则注册
2、动态注册:在.cpp的JNI_OnLoad方法里注册
JNI接口规范的命名规则:
Java_PackageName_ClassName_MethodName
当我们在Java中调用native方法时,JVM也会根据这种命名规则来查找、调用native方法对应的C方法

JNIENV

JNIENV代表了Java环境,通过(*JNIEnv)就可以对Java端的代码进行操作比如:
1、创建Java的对象
2、调用Java对象的方法
3、获取java对象的属性等
我们可以通过jni.h查看可知:
在这里插入图片描述
在这里插入图片描述
JNIEnv指向_JNIEnv,而_JNIEnv是定义的一个C++结构体,里面包含了很多通过JNI接口对象调用的方法。

jobject

jobject代表了定义native函数的Java类或java类的实例:
1、如果native函数是static,则代表类Class对象
2、如果native函数是非static,则代表类的实例对象
我们可以通过jobject访问定义该native方法的成员方法、成员变量等。

java、JNI、C/C++数据类型映射关系

基本数据类型转化关系

在java中调用jni的native方法传递的参数是java类型的,这些参数必须经过Dalvik虚拟机转换成JNI类型的才能被JNI层所识别,如图
在这里插入图片描述

引用类型转换关系

JNI的引用类型定义了九种数组类型,以及jobject、jclass、jstring、jthrowable四种类型,它们的继承关系如下图:
在这里插入图片描述
它们与java类型的对应关系如下:
在这里插入图片描述

动态注册

动态注册的原理

直接告诉native函数其在JNI中对应函数的指针

动态注册的原理是这样的:JNI允许我们提供一个函数映射表,注册给JVM,这样JVM就可以用函数映射表来调用相应的函数,而不必通过函数名来查找相关函数(这个查找效率很低,函数名超级长)。
实现过程:
1、利用结构体JNINativeMethod保存Java Native函数和JNI函数的对应关系
2、在一个JNINativeMehod数组中保存所有native函数和JNI函数的对应关系
3、在Java中通过System.loadLibrary加载完JNI动态库后,调用JNI_OnLoad函数,开始动态注册
4、JNI_OnLoad中会调用(*env)->FindClass找到本地java类
5、JNI_OnLoad中调用(*env)->RegisterNatives函数进行函数注册

动态注册的步骤:
1、创建JNIDemo工程,编写java类,例如:MainActivity.java
2、build project 整个工程,在build\intermediates\javac\debug\classes\包名 文件下会生成MainActivity.class文件
3、在build\intermediates\javac\debug\classes 目录下打开命令行,输入 javah -jni 包名.MainActivity(全类名)生成包名MainActivity.h文件
4、在app目录下新建jni文件夹,在jin文件夹下创建register.c文件
5、编写register.c文件,并导入jni.h,包名_MainActivity.h头文件,重写JNI_OnLoad函数。

6、在register.c文件中创建JNINativeMethod数组,数组里面每一个元素JNI映射函数,这个我们后面会详细讲解。
7、在JNI_OnLoad函数里面需要完成以下四步:
- 通过(*vm)->GetEnv获取JVM的JNIEnv的属性信息
- 通过(*env)->FindClass方法找到对应的本地类
- 通过(*env)->RegisterNatives方法注册java本地类中的方法
- 通过return 返回值指定指定Jni版本

8、在jni文件夹下创建CMakeLists.txt文件,并在CMakeLists.txt文件中指定要生成的so库名称以及要使用的register.c源文件
9、在build.gradle文件中添加cmkeLists.text文件的路径,
10、运行so库。

JNINativeMethod

Android中使用了一种与传统不同的java JNI的方式的来定义其native的函数。
其中很重要的区别是Android使用了一种java和C函数的映射表数组,并在其中描述了函数的参数和返回值。这个数组的类型是JNINativeMethod,JNINativeMethod其实是一个结构体,在jni.h头文件中定义,通过这个结构体从而使java与jni建立联系,定义如下:

typedef struct {const char* name; //Java中函数的名字const char* signature;//符号签名,描述了函数的参数和返回值void* fnPtr;//函数指针,指向一个被调用的函数} JNINativeMethod;
  • 第一个变量name 是java中函数的名字
  • 第二个变量signature,是字段描述符(签名)或者函数描述符(签名)
  • 第三个变量fnotr是函数指针,指向C函数。Void*是万能指针,可以理解相当于java中的泛型
    下面我们重点讲一下第二个变量signature也就是签名。

签名

什么是签名

所谓函数签名,简单点的理解可以理解成一个函数的唯一标识,一个签名对应着一个函数的签名。这个是一一对应的关系。有些人可能会问:函数名不能作为标识吗?答案是否定的,因为java支持重载

为什么要有签名呢?

我们知道,java是支持函数重载的。一个类里面可以有多个同名但是不同参数的函数,所以函数名+参数名才能构成一个函数的表示,因此我们需要针对参数做一个签名标识。这样jni层才能唯一识别到这个函数。

如何获取函数的签名

函数的签名是针对函数的参数以及返回值进行组成的。它遵循如下格式(参数类型1;参数类型2;参数类型3…)返回值类型。
目前我知道获取签名的两种方式:1、生成的.h文件,对应函数上面的注释
2利用命令:javap -s -p TextDemo.class

基本数据类型签名对照表
类型标识                       Java类型
Z                             boolean
B                             byte
C                             char
S                             short
I                             int
J                             long
F                             float
D                             double
L/java/language/String        String
[I                            int[]
[Ljava/lang/object            Object[]
V                             void

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/293394.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在Linux系统中安装MySQL数据库

目录 一、MySQL简介 二、MySQL安装步骤 1、下载MySQL的YUM仓库文件 2、安装MySQL源 3、解决密钥异常问题 4、安装MySQL服务器 5、开启MySQL服务 6、查看MySQL服务器中root用户的初始密码 7、使用初始密码登录MySQL服务器 8、修改root用户登录MySQL服务器的密码 三、…

【圣诞】极安云科赠书活动第①期:CTF实战:从入门到提升

【圣诞】极安云科赠书活动第①期:CTF实战:从入门到提升 9787111724834 9787121376955 9787302556275 ISBN编号:9787111724834 书名:CTF实战:从入门到提升 定:99.00元 开本:184mm260&#xff…

自制数据库空洞率清理工具-C版-01-EasyClean-V1.0(支持南大通用数据库Gbase8a)

目录 一、环境信息 二、简述 三、支持功能 四、空洞率 五、工具流程图 六、安装包下载地址 七、参数介绍 1、命令模板 2、命令样例 3、参数表格 八、安装步骤 1、配置环境变量 2、生效环境变量 3、检验动态链接是否正常 九、运行效果 一、环境信息 名称值CPUInt…

python中整数和浮点数的运算

任意两个数相除时,结果总是浮点数,即便这两个数能够整除。例如: 在任何运算中,只要有操作数是浮点数,结果总是浮点数。例如:

3.[BUU]warmup_csaw_20161

1.checksec 检查文件类型 ELF-64-little ,无其他限权,直接用ida检查代码。 2.IDA进行反编译,进行代码审计 查看各个名称的内容: 了解基本攻击思路: 攻击思路:gets输入垃圾数据覆盖v5内容,再将s…

Flink 运行时[Runtime] 整体架构

一、基本组件栈 在Flink整个软件架构体系中,同样遵循着分层的架构设计理念,在降低系统耦合度的同时,也为上层用户构建Flink应用提供了丰富且友好的接口。从下图中可以看出整个Flink的架构体系基本上可以分为三层,由上往下依次是 …

C#文件操作(二)

一、前言 文章的续作前文是: C#文件操作(一)-CSDN博客https://blog.csdn.net/qq_71897293/article/details/135117922?spm1001.2014.3001.5501 二、流 流是序列化设备的抽象表示序列化设备可以线性方式储存数据并可按照同样的方式访问一次…

[微服务 ]微服务集成中的3个常见缺陷,以及如何避免它们

微服务风靡一时。他们有一个有趣的价值主张,即在与多个软件开发团队共同开发的同时,将软件快速推向市场。因此,微服务是在扩展您的开发力量的同时保持高敏捷性和快速的开发速度。 简而言之,您将系统分解为微服务。分解并不是什么新…

08、基于LunarLander登陆器的DDQN强化学习(含PYTHON工程)

08、基于LunarLander登陆器的DDQN强化学习(含PYTHON工程) LunarLander复现: 07、基于LunarLander登陆器的DQN强化学习案例(含PYTHON工程) 08、基于LunarLander登陆器的DDQN强化学习(含PYTHON工程&#xf…

网络故障?只需使用这个ChatGPT插件,解决率高达90%

1. 在使用 ChatGPT 时遇到的一些问题 近期,ChatGPT 受到了极大的关注,众多用户纷至沓来。仅用五天,ChatGPT 的用户数量就突破了 1 亿。下图展示了国外 TRT World 媒体统计的各大平台突破 1 亿用户的对比图。 如此迅速的用户增长也带来了一些…

超级逼真人脸生成,Stable Diffusion的3个关键技巧

大家好,你是否曾想过,为什么别人可以使用AI图像生成技术生成如此逼真的人脸,而自己的尝试却充满了错误和瑕疵,让人一眼看出是假的。尝试过调整提示和设置,但似乎仍无法与他人的质量相匹配。 本文将带大家了解使用Stab…

WPF组合控件TreeView+DataGrid之DataGrid封装

(关注博主后,在“粉丝专栏”,可免费阅读此文) wpf的功能非常强大,很多控件都是原生的,但是要使用TreeViewDataGrid的组合,就需要我们自己去封装实现。 我们需要的效果如图所示&#x…