JNI 详细介绍

一 介绍 

java调⽤c++,c++代码可以通过JNIEnv执行java代码。

安卓NDK 已经对JNI环境进行了集成,我们可以通过android studio来快速搭建一个项目。

二 项目搭建

打开android studio 创建工程,创建工程选择模板Native C++

 三 模板格式介绍

生成的模板java类如下

public class MainActivity extends AppCompatActivity {// Used to load the 'jnidemo' library on application startup.static {System.loadLibrary("jnidemo");}private ActivityMainBinding binding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());// Example of a call to a native methodTextView tv = binding.sampleText;tv.setText(stringFromJNI());}/**这个方法的实现在c++测*/public native String stringFromJNI();
}

native 标记的方法没有方法体,表示在c++测实现,

对应c++测的代码如下:

extern "C"   下⾯的⽅法采⽤C的编译⽅式
JNIEXPORT // JNIEXPORT 关键字 标记该⽅法可以被外部调⽤
jstring // 返回值类型 对应 java 测的 String
JNICALL // 关键字 jni call 约束函数⼊栈顺序,和堆栈内存清理规则
Java_com_example_jnidemo_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}

那长串的名字

Java_com_example_jnidemo_MainActivity_stringFromJNI

红色为包名,绿色为类名,蓝色为方法名。

JNIEnv是c++ 和java沟通的桥梁,env就代表了java环境

四 签名和类型

你们肯定很懵,下面的表是什么,先不管它,请先硬着往下看

java类型对应在c++代码处的类型:

 

typedef void* jobject;  /*对应任何Java对象,通常对应⼀个类的实例*
typedef jobject jclass;  /*对应Java类对象,⽐如Student类*/
typedef jobject jstring;  /*java.lang.String*/
typedef jobject jarray;   /*数组*/typedef jarray jobjectArray; /*任何对象的数组*/
typedef jarray jbooleanArray; /*boolean []*/
typedef jarray jbyteArray;  /*byte []*/
typedef jarray jcharArray; /*char []*/
typedef jarray jshortArray; /*short []*/
typedef jarray jintArray;  /*int []*/
typedef jarray jlongArray; /*long []*/
typedef jarray jfloatArray; /*float []*/
typedef jarray jdoubleArray; /*double []*/
typedef jobject jthrowable; /*java.lang.Throwable*/
typedef jobject jweak;      /**/

五 函数调用

java 直接调用native 的方法,会执行c++处的实现,c++也可以通过env调用java处的代码。

c++侧执⾏java⽅法

hello,word 

便于理解我java和c++ 代码放在了一块展示,请小白对比看

//java 处代码
public void hello(){System.out.println("hello,word!");
}
public native void runHello();// c++ 处代码
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_fuc_TestMain_runHello(JNIEnv *env, jobject thiz)// env是和java沟通的桥梁// thiz代表 java对象this, 谁调用native,那么thiz就是那个对象的thiz  // 比如 A.runHello(),那么thiz就是A对象// 获得Class 对象 jclass thisClass= env->GetObjectClass(thiz);// 根据方法名字、参数、返回类型; 获得 hello ⽅法id,// "hello" 为方法名字// 后面的()V  () 代表参数为空,V代表返回值为void jmethodID helloMethodID= env->GetMethodID(thisClass,"hello", "()V");// 根据方法id来执行方法,执行 thiz的hello方法env->CallVoidMethod(thiz,helloMethodID);
}

c++执行java 测的有参函数

// java 处代码
private int add(int a,int b){return a+b;
}
public native int runAdd(int a,int b);// c++ 处代码
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_fuc_TestMain_runAdd(JNIEnv *env, jobject thiz, jint a,jint b){// 获得Class 对象// jclass 表示java Class 类型jclass thisClass= env->GetObjectClass(thiz);// 获取add ⽅法id// (II)I 表示参数类型 第一个I表示第一个参数 类型为int类型,第二个I表示第二个参数为int类型,// 括号外面那个I 表示返回类型为I即int// 方法签名对应请看 上文第四节jmethodID addMethodID= env->GetMethodID(thisClass,"add", "(II)I");// 运⾏ add(a,b) // jint 对应java int类型jint result= env->CallIntMethod(thiz,addMethodID,a,b);return result;
}// test 处的测试方法
// 测试 add
@Test
public void testAdd(){// 实际上运行c++处的实现,c++处的实现又去调用java的 add方法int result = testMain.runAdd(4, 2); System.out.println("addResult:"+result);
}

这么一看是不是非常像反射调用执行方法,我们对比一下反射学习:

// 反射执⾏
@Test
public void load() throws Exception{// 获得class对象Class<? extends TestMain> aClass = testMain.getClass();// 获得add⽅法Method add = aClass.getDeclaredMethod("add", int.class, int.class);// 设置可⻅性add.setAccessible(true);// 运⾏add⽅法Object invoke = add.invoke(testMain,2, 4);System.out.println("执⾏结果:"+invoke);
}

是的,非常类似,都是先获得class对象,然后获得方法,再执行方法。

修改成员属性值

在c++处修改 java对象的成员变量

// java 处
public  int age;public int getAge() {return age;
}
public native void setAge();// c++ 处
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_fuc_TestMain_setAge(JNIEnv *env, jobject thiz) {// 获得class对象jclass thisClass= env->GetObjectClass(thiz);// 获得 属性IDjfieldID ageID=env->GetFieldID(thisClass,"age", "I");// 执⾏修改env->SetIntField(thiz,ageID,35);
}// test
@Test
public void testSetAge(){System.out.println("修改前:"+testMain.getAge());testMain.setAge();System.out.println("修改后:"+testMain.getAge());
}

创建对象

在c++侧创建⼀个List数组,并向它添加"A","B"元素

// java
public native List<String> getStringArray();// C++
extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_myapplication_fuc_TestMain_getStringArray(JNIEnv *env, jobject){// 获得ArrayList Class 对象jclass listClass= env->FindClass("java/util/ArrayList");// 获得ArrayList 构造⽅法id// 构造方法签名统一为 <init>jmethodID initMethodID= env->GetMethodID(listClass,"<init>", "()V");// 创建 ArrayList 对象,相当于在java处那么 new了一个对象jobject list= env->NewObject(listClass,initMethodID);// 获得add ⽅法idjmethodID addMethodID= env->GetMethodID(listClass,"add", "(ILjava/lang/Object;)V");// 创建 A 和 Bjstring A= env->NewStringUTF("A");jstring B= env->NewStringUTF("B");// 添加元素env->CallVoidMethod(list,addMethodID,0,A);env->CallVoidMethod(list,addMethodID,1,B);// 释放本地引⽤// 只是释放c++ 测的引用,并没有删除 java那么对应的对象env->DeleteLocalRef(A);env->DeleteLocalRef(B);return list;
}// 在 c++ 的实现相当于执⾏了如下java 代码
ArrayList<Object> list = new ArrayList<>();
list.add(0,"A");
list.add(1,"B");

创建全局引⽤

1. 全局引⽤:全局引⽤在整个Java虚拟机中都有效,只要它们还在被使⽤,它们就不会被垃圾回收。全局引⽤在JNI代码结束时必须显式删除。

jobject globalRef = env->NewGlobalRef(localRef);// ...
env->DeleteGlobalRef(globalRef);

2. 本地引⽤:本地引⽤只在单个JNI调⽤期间有效。当JNI⽅法返回时,所有的本地引⽤都会⾃动被删除。(保险起⻅需要⼿动删除)

jobject localRef = env->NewLocalRef(tem);// ...
env->DeleteLocalRef(localRef);

异常处理

在JNI中,异常处理有点不⼀样,当在c++调⽤java⽅法抛出异常时候,函数不会直接捕获异常,异常会直接挂起,需要⼿动处理
• ExceptionCheck :这个⽅法⽤于检查是否有未处理的异常。它返回⼀个布尔值,如果存在未
处理的异常,则返回 true ,否则返回 false 。
• ExceptionOccurred :这个⽅法⽤于检查是否有异常发⽣。它返回⼀个指向异常对象的引⽤,
如果没有异常发⽣,则返回 nullptr 。
应该在执⾏⽅法时候都做⼀个异常处理,防⽌程序崩溃

 

// java
// 抛出运⾏异常
public int throwError(){return 1/0;
}public native void tryError();// c++
// 抛出异常
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_fuc_TestMain_tryError(JNIEnv *env, jobject thiz){// 执⾏异常代码jclass thisClass= env->GetObjectClass(thiz);jmethodID throwErrorMethodId= env->GetMethodID(thisClass,"throwError","()I");// 异常jint result= env->CallIntMethod(thiz,throwErrorMethodId);// 是否有异常if(env->ExceptionOccurred()!= nullptr){// 输出异常信息env->ExceptionDescribe();// 清除挂起的异常env->ExceptionClear();// 创建 RuntimeException 对象jclass runErrorClass = env->FindClass("java/lang/RuntimeException");// 抛出异常 消息"ERROR!"env->ThrowNew(runErrorClass,"ERROR!");return;}
}// 测试
@Test
public void testError(){try {testMain.tryError();}catch (Exception e){System.out.println("有异常");e.printStackTrace();}
}

六 总结



c++ 调⽤java⽅法
        1、获得class对象
        2、获得对象中的⽅法id
        3、执⾏⽅法


c++创建java对象
        1、获得class对象
        2、获得构造⽅法id
        3、执⾏构造⽅法


异常处理流程
        c++层调⽤执⾏java对象⽅法
        异常处理




如果认为对你有帮助,欢迎点赞收藏 

如果认为对你有帮助,欢迎点赞收藏 

如果认为对你有帮助,欢迎点赞收藏 

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

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

相关文章

非关系型数据库Redis

文章目录 一&#xff0c;关系型数据库和非关系型数据可区别1.关系型数据库2.非关系型数据库3.区别3.1存储方式3.2扩展方式3.2事务性的支持 二&#xff0c;非关系型数据为什么产生三&#xff0c;Redis1.Redis是什么2.Redis优点3.Redis适用范围4. Redis 快的原因4.1 基于内存运行…

直播标准权威发布,阿里云RTS获首批卓越级评估认证

近期举办的2024“可信云大会”上&#xff0c;中国信通院正式发布了2024年上半年音视频领域最新评估结果。阿里云超低延时直播&#xff0c;以首批卓越级&#xff0c;通过中国信通院超低延时直播性能及服务质量分级测试。 标准发布&#xff0c;权威量化直播体验质量 从直播元年发…

神经网络通俗理解学习笔记(0) numpy、matplotlib

Numpy numpynumpy 基本介绍Ndarray对象及其创建Numpy数组的基础索引numpy数组的合并与拆分&#xff08;重要&#xff09;numpy数组的矩阵运算Numpy数组的统计运算numpy中的arg运算numpy中的神奇索引和比较 Matplotlib numpy numpy 基本介绍 numpy 大多数机器学习库都用了这个…

【Echarts】使用多横坐标轴展示近十五天天气预报

现在手机都有天气app,使用echarts展示十五天天气预报的需要你遇到过这样离大谱的需求吗&#xff1f;如果没有或许你能从中找到些许思路。 效果 看效果是不是有点那么个意思,开局一张图,代码全靠ctrl c。不多说上代码。 vue模板引擎代码 <template><div ref"xA…

从头开始学MyBatis—02基于xml和注解分别实现的增删改查

首先介绍此次使用的数据库结构&#xff0c;然后引出注意事项。 通过基于xml和基于注解的方式分别实现了增删改查&#xff0c;还有获取参数值、返回值的不同类型对比&#xff0c;帮助大家一次性掌握两种代码编写能力。 目录 数据库 数据库表 实体类 对应的实体类如下&#x…

【LeetCode每日一题】——912.排序数组

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 优先队列 二【题目难度】 中等 三【题目编号】 912.排序数组 四【题目描述】 给你一个整数…

H5 three.js 实现六年级观察物体

o(&#xffe3;▽&#xffe3;)ブ 我又带着新的demo来啦~ 预览 功能点 立方体的阴影 立方体的添加 位置记录 最大限制 三视图展示 立方体的移除 答题模式 随机出题 题库出题 源码 注释算是比较全了&#xff0c;可能部分会有点绕&#xff0c;还能够再优化一下~ <!DOCTYPE …

【第35章】Spring Cloud之Seata-Server快速入门

文章目录 前言一、准备1. 架构图2. 工作机制3. Seata术语4. 事务模式4.1 Seata AT 模式(依赖数据库)4.2 Seata TCC 模式(不依赖数据库)4.3 Seata Saga 模式(支持长事务)4.4 Seata XA 模式(支持XA 协议) 二、安装1. 下载2. 解压3. 重要属性4. 修改配置4.1 配置中心4.2 注册中心4…

C语言 13 指针

指针可以说是整个 C 语言中最难以理解的部分了。 什么是指针 还记得在前面谈到的通过函数交换两个变量的值吗&#xff1f; #include <stdio.h>void swap(int, int);int main() {int a 10, b 20;swap(a, b);printf("a %d, b %d", a, b); }void swap(int …

循环神经网络RNN+长短期记忆网络LSTM 学习记录

循环神经网络&#xff08;RNN) RNN的的基础单元是一个循环单元&#xff0c;前部序列的信息经处理后&#xff0c;作为输入信息传递到后部序列 x为输入向量&#xff0c;y为输出向量&#xff0c;a为上一隐藏层的a与x通过激活函数得到的值&#xff0c;简言之&#xff0c;每一层神…

华为 HCIP-Datacom H12-821 题库 (23)

&#x1f423;博客最下方微信公众号回复题库,领取题库和教学资源 &#x1f424;诚挚欢迎IT交流有兴趣的公众号回复交流群 &#x1f998;公众号会持续更新网络小知识&#x1f63c; 1.以下关于 VRRP 基本概念的描述&#xff0c;错误的是哪些选项&#xff1f; A、一个虚拟路由器…

S32K3 工具篇6:如何将RTD EB工程导入到S32DS

S32K3 工具篇6&#xff1a;如何将RTD EB工程导入到S32DS 1. MCAL_Plugins->Link Source Resource Filters2. Includes3. Preprocessor4. Linker5. optimization6. main.c 这个主题实际上&#xff0c;之前已经有多人写过&#xff0c;并且写的很好&#xff0c;只是实际操作中&…

qt-creator-10.0.2之后版本的jom.exe编译速度慢下来了

1、Qt的IDE一直在升级&#xff0c;qt-creator的新版本下载地址 https://download.qt.io/official_releases/qtcreator/ 2、本人一直用的是qt-creator-10.0.2版本&#xff0c;官网历史仓库可以下载安装包qt-creator-opensource-windows-x86_64-10.0.2.exe https://download.qt…

URP 线性空间 ui资源制作规范

前言&#xff1a; 关于颜色空间的介绍&#xff0c;可参阅 unity 文档 Color space URP实现了基于物理的渲染&#xff0c;为了保证光照计算的准确&#xff0c;需要使用线性空间&#xff1b; 使用线性空间会带来一个问题&#xff0c;ui资源在unity中进行透明度混合时&#xff…

COMP 6714-Info Retrieval and Web Search笔记week1

哭了哭了&#xff0c;这周唯一能听懂的就这门 目录 IR&#xff08;Information Retrieval)是什么&#xff1f;IR的基本假设Unstructured (text) vs. structuredDocuments vs. Database Records比较文本&#xff08;Comparing Text&#xff09;IR的范围(Dimensions of IR)IR的任…

YoloV10改进策略:上采样改进|动态上采样|轻量高效,即插即用(适用于分类、分割、检测等多种场景)

摘要 本文使用动态上采样改进YoloV10,动态上采样是今天最新的上采样改进方法,具有轻量高效的特点,经过验证,在多个场景上均有大幅度的涨点,而且改进方法简单,即插即用! 论文:《DySample:Learning to Upsample by Learning to Sample》 论文:https://arxiv.org/pdf/…

fmql之ubuntu移植

官方资料&#xff1a;ubuntu18的压缩包 目的&#xff1a;放到SD卡中启动ubuntu&#xff08;官方是放在emmc中&#xff09; 教程&#xff1a;99_FMQL45_大黄蜂开发板跑ubuntu18.04.docx 所需文件 其中&#xff0c;format_emmc_ext4.txt对emmc的分区是512M&#xff08;放上述文…

C++ | Leetcode C++题解之第397题整数替换

题目&#xff1a; 题解&#xff1a; class Solution { public:int integerReplacement(int n) {int ans 0;while (n ! 1) {if (n % 2 0) {ans;n / 2;}else if (n % 4 1) {ans 2;n / 2;}else {if (n 3) {ans 2;n 1;}else {ans 2;n n / 2 1;}}}return ans;} };

如何查看串口被哪个程序占用?截止目前最方便的方法

痛点&#xff1a;串口因为某种原因被占用&#xff0c;如何找到罪魁祸首&#xff1f; 做开发的小伙伴们&#xff0c;经常会遇到这样的问题&#xff1a;串口因为某种原因被占用&#xff0c;导致无法通讯&#xff0c;但是又找不到被哪个程序占用。只有重启电脑&#xff0c;才能解…

CSS“多列布局”(补充)——WEB开发系列35

多列布局是一种非常常见的布局方式&#xff0c;适用于内容丰富的页面&#xff0c;如新闻网站、杂志或博客。 一、CSS多列布局概述 CSS多列布局允许我们将内容分成多个垂直列&#xff0c;使页面布局更加灵活和多样化。多列布局的主要属性包括 ​​column-count​​、​​column…