JNI 详细介绍

devtools/2024/9/20 6:20:25/ 标签: android, JNI, c++, java调用c++, java, java执行c++, env

一 介绍 

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

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

二 项目搭建

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

 三 模板格式介绍

生成的模板java类如下

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++ 代码放在了一块展示,请小白对比看

javascript">//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 测的有参函数

javascript">// 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);
}

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

javascript">// 反射执⾏
@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对象的成员变量

javascript">// 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"元素

javascript">// 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代码结束时必须显式删除。

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

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

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

异常处理

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

 

javascript">// 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.ppmy.cn/devtools/112718.html

相关文章

Python数据分析及可视化教程--商城订单为例-适用电商相关进行数据分析---亲测可用!!!!

前言:Python 是进行数据分析和可视化的强大工具,常用的库包括 Pandas、NumPy、Matplotlib 和 Seaborn。以下是一个基本的教程概述,介绍了如何使用这些库来进行数据分析和可视化: Python数据分析及可视化教程 1、 环境准备2、数据准备3、开始数据分析3.1、导入库3.2、加载数…

深入理解数据分析的使用流程:从数据准备到洞察挖掘

数据分析是企业和技术团队实现价值的核心。 5 秒内你能否让数据帮你做出决策&#xff1f; 通过本文&#xff0c;我们将深入探讨如何将原始数据转化为有意义的洞察&#xff0c;帮助你快速掌握数据分析的关键流程。 目录 数据分析的五个核心步骤1. 数据获取常用数据获取方式 2. 数…

《C++模板元编程:高效实现编译期斐波那契数列计算》

在 C的神秘世界里&#xff0c;模板元编程犹如一把神奇的钥匙&#xff0c;能打开许多高性能编程的大门。今天&#xff0c;我们就来深入探讨如何在 C的模板元编程中实现一个在编译期计算斐波那契数列的算法&#xff0c;同时确保在面对非常大的输入时不会导致编译时间过长。 一、…

【开发环境搭建】Macbook M1搭建Java开发环境

JDK 安装与配置 下载并安装 JDK&#xff1a; ARM64 DMG 安装包下载链接&#xff1a;JDK21 for Mac (ARM64)。双击下载的 DMG 文件&#xff0c;按照提示安装 JDK。 配置环境变量&#xff1a; 打开终端&#xff0c;使用 vim 编辑 .bash_profile 文件&#xff1a; vim ~/.bash_pr…

_Array类,类似于Vector,其实就是_string

例子&#xff1a; using namespace lf; using namespace std;int main() {_Array<int> a(10, -1);_Array<_string> s { _t("one"), _t("two") };_pcn(a);_pcn(s);} 结果&#xff1a; 源代码_Array.h&#xff1a; /***********************…

直播相关03-录制麦克风声音, ffmpeg 命名,使用命令行完成录音

一 ffmpeg 命令 ffmpeg arg1 arg2 -i arg3 arg4 arg5ffmpeg 全局参数 输入文件参数 -i 输入文件 输出文件参数 输出文件arg1&#xff1a;全局参数 arg2&#xff1a;输入文件参数 arg3&#xff1a;输入文件 arg4&#xff1a;输出文件参数 arg5&#xff1a;输出文件 二 ffprobe …

根据NVeloDocx Word模板引擎生成Word(四)

前面介绍了《E6低代码开发平台》的Word模版引擎NVeloDocx&#xff0c;实现了表单的基本字段、子表、单张图片、二维码、条形码怎么基于NVelocity脚本输出到Word文件&#xff0c;都是些比较简单且常用的需求。 本篇介绍怎么基于NVeloDocx在Word中插入图表&#xff0c;目前只支持…

HarmonyOS Next鸿蒙NDK使用示例

创建一个Native C项目 跟普通项目相比&#xff0c;主要区别是多了一个cpp文件夹、oh-package.json5中的dependencies引入还有build-profile.json5中的externalNativeOptions配置&#xff0c;abiFilters是支持的CPU架构&#xff0c;目前移动端项目只支持arm64-v8a、x86_64两种。…

笔试强训day07

在字符串中找出连续最长的数字串 #include <bits/stdc.h>using namespace std; const int N 500; char s[N]; bool check(char c) {return c > 0 && c < 9; } int main() {scanf("%s", s);int l -1, r -1;int n strlen(s);int left 0, rig…

Spring Boot 常用注解

1. 基础 Spring 注解 Component 标记一个类作为 Spring IoC 容器的一个组件。Repository 标记一个 DAO 类&#xff0c;同时提供了异常转换机制。Service 标记业务逻辑层的服务类。Controller 标记一个 Web 层的控制器类。RestController 结合了 Controller 和 ResponseBody&am…

GO Govaluate

govaluate 是一个用于在 Go 语言中动态求值表达式的库。它允许你解析和评估字符串形式的表达式&#xff0c;这些表达式可以包含变量、函数以及逻辑、算术和比较操作。它非常适合在运行时处理复杂的逻辑规则和条件表达式&#xff0c;而不需要重新编译代码。 安装 govaluate go…

C语言自定义类型结构体(24)

文章目录 前言一、结构体类型的声明结构体回顾结构体的特殊声明结构体的自引用 二、结构体的内存对齐对齐规则为什么存在内存对齐&#xff1f;修改默认对齐数 三、结构体传参四、结构体实现位段什么是位段位段的内存分配位段的跨平台问题位段的应用位段使用的注意事项 总结 前言…

Linux学习-Ansible(一)

环境- Rocky-Linux8.6 安装部署Ansible # 安装ansible [rootharbor ansible]# dnf install -y ansible-core #查看安装信息 [rootharbor ansible]# ansible-doc --version ansible-doc [core 2.12.2]config file /root/ansible/ansible.cfgconfigured module search path […

动态规划---不相交的线

题目&#xff1a; 在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 现在&#xff0c;可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线&#xff0c;这些直线需要同时满足&#xff1a; nums1[i] nums2[j]且绘制的直线不与任何其他连线&#xff08;非水…

SQLITE3数据库实现信息的增删改查

#include <myhead.h> #include <sqlite3.h> typedef struct { int id; char name[20]; int age; int money; }woker; int callbake(void *arg,int n,char **a,char **b)//回调 输出查找到的工人信息 { for(int i 0;i<n;i) { …

[数据集][目标检测]汽车头部尾部检测数据集VOC+YOLO格式5319张3类别

数据集制作单位&#xff1a;未来自主研究中心(FIRC) 版权单位&#xff1a;未来自主研究中心(FIRC) 版权声明&#xff1a;数据集仅仅供个人使用&#xff0c;不得在未授权情况下挂淘宝、咸鱼等交易网站公开售卖,由此引发的法律责任需自行承担 数据集格式&#xff1a;Pascal VOC格…

Linux05

1.echo命令 echo是输出命令&#xff0c;类似printf 例如&#xff1a;echo "hello world"&#xff0c;输出hello world echo pwd&#xff0c;输出pwd的位置。是键盘上~ 2.重定向符> >> >指把左边内容覆盖到右边 echo hello world>test.txt >…

MATLAB在嵌入式系统设计中的最佳实践

嵌入式系统设计是一个复杂的过程&#xff0c;涉及硬件和软件的紧密集成。MATLAB提供了一套全面的解决方案&#xff0c;从算法开发到代码生成&#xff0c;再到硬件验证&#xff0c;极大地简化了这一过程。本文将探讨使用MATLAB进行嵌入式系统设计的最佳实践&#xff0c;包括模型…

Vue Router push方法的使用

Vue Router push方法的使用 this.$router.push 是 Vue Router 提供的一个方法,用于在 Vue.js 应用中进行编程式导航。它的作用是将用户导航到应用中的不同路由。 基本作用 this.$router.push 方法会在浏览器历史记录中添加一个新的记录,并导航到指定的路由。它的工作方式类…

深度学习中常见的损失函数

在机器学习和深度学习中&#xff0c;损失函数用于衡量模型预测值与真实值之间的差异。根据任务的类型&#xff08;如回归、分类等&#xff09;&#xff0c;可以使用不同的损失函数。下面列举了一些常见的损失函数&#xff1a; 1. 回归问题中的损失函数 回归任务的目标是预测连…