Android JNI复杂用法,回调,C++中调用Java方法
Android JNI复杂用法,回调,C++中调用Java方法
一、前言
Android JNI的 普通用法估计很多人都会,但是C++中调用Java方法很多人不熟悉,并且网上很多介绍都是片段的。
虽然C/C++调用Java不常用,但是掌握多一点还是有好处的。
Android JNI的基础知识介绍,之前已经有介绍,不熟悉的可以先看看:
Android Jni的介绍和简单Demo实现:
https://blog.csdn.net/wenzhi20102321/article/details/136291126
本文主要介绍JNI C++调用Java代码实现和相关知识,有兴趣的可以看看。
二、C++调用Java方法实现代码
1、上层代码 MainAcitvity.java
package com.demo.jnicallback;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;public class MainActivity extends AppCompatActivity {String TAG = "MainActivity.java";static {System.loadLibrary("native-lib");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.i(TAG, "conCreate");TextView tv = findViewById(R.id.sample_text);String jniString = stringFromJNI();Log.i(TAG, "conCreate cppCallBackMethod jniString = " + jniString);tv.setText("" + jniString);}//C++调用Java 的方法,定义成private方法,cpp也是可以调用到的,因为是通过反射过来的public void cppCallBackMethod(String name, int age) {Log.i(TAG, "cppCallBackMethod name = " + name + ",age = " + age);}//Java 调用到 cpp 的native方法public native String stringFromJNI();}
布局上未做修改,运行后的默认字符串"Hello from C++"。
Java代码这里加了一个给C++调用过来的方法,具体实现效果可以看是日志。
2、cpp代码 native-lib.cpp 代码:
#include <jni.h>
#include <string>#include <android/log.h> //添加头文件
#define LOG_TAG "native-lib.cpp" //定义TAG
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)#include <iostream>
#include <chrono>
#include <thread>extern "C" JNIEXPORT jstring JNICALL
Java_com_demo_jnicallback_MainActivity_stringFromJNI(JNIEnv* env,jobject thiz /* this */) {std::string hello = "Hello from C++";LOGI("stringFromJNI hello = %s", hello.c_str());//c++调用Java方法:public void cppCallBackMethod(String name, int age)jobject m_object = env->NewGlobalRef(thiz);//创建对象的本地变量jclass mainActivityCls=env->FindClass("com/demo/jnicallback/MainActivity");//获取类对象jmethodID cppCallBackMethod = env->GetMethodID(mainActivityCls, "cppCallBackMethod", "(Ljava/lang/String;I)V");const char *message = "cppA";int age = 10;env->CallVoidMethod(m_object, cppCallBackMethod, env->NewStringUTF(message), age);while (age < 50) {//睡眠1秒std::chrono::seconds duration(1); // 休眠一秒钟std::this_thread::sleep_for(duration);age = age + 10;env->CallVoidMethod(m_object, cppCallBackMethod, env->NewStringUTF(message), age);}return env->NewStringUTF(hello.c_str());
}
上面代码可以看到获取类对象,是为了获取方法id;获取对象的本地变量是为了调用方法。
网上有些示例可能写法不一样,熟悉c++代码的应该知道,"env->“的写法和”(*env)."是一个意思。
3、效果日志:
//Java打印最开始日志
2024-03-01 16:27:50.401 I/MainActivity.java: conCreate
//cpp文件打印,开始的日志
2024-03-01 16:27:50.402 I/native-lib.cpp: stringFromJNI hello = Hello from C++
//cpp调用Java部分日志,在Java代码每隔一秒的打印
2024-03-01 16:27:50.402 I/MainActivity.java: cppCallBackMethod name = cppA,age = 10
2024-03-01 16:27:51.403 I/MainActivity.java: cppCallBackMethod name = cppA,age = 20
2024-03-01 16:27:52.403 I/MainActivity.java: cppCallBackMethod name = cppA,age = 30
2024-03-01 16:27:53.403 I/MainActivity.java: cppCallBackMethod name = cppA,age = 40
2024-03-01 16:27:54.404 I/MainActivity.java: cppCallBackMethod name = cppA,age = 50
//Java onCreate最后的日志,打印C++返回的字符串
2024-03-01 16:27:54.404 I/MainActivity.java: conCreate cppCallBackMethod jniString = Hello from C++
上面的代码就有Java --> C++和C++ --> Java的代码流程。
注意,这里的示例代码添加了睡眠代码,如果在主线程长时间执行任务是有可能导致ANR的。
4、cpp代码 native-lib.cpp 代码另一种写法
下面这种写法不用NewGlobalRef创建对象的本地变量。
中间的区别就是这里函数的调用没有使用"->“,使用的”(*env)."
extern "C" JNIEXPORT jstring JNICALL
Java_com_demo_jnicallback_MainActivity_stringFromJNI(JNIEnv* env,jobject thiz /* this */) {std::string hello = "Hello from C++";LOGI("stringFromJNI hello = %s", hello.c_str());//c++调用Java方法:public void cppCallBackMethod(String name, int age)jclass mainActivityCls=(*env).FindClass("com/demo/jnicallback/MainActivity");//获取类对象jmethodID cppCallBackMethod = (*env).GetMethodID(mainActivityCls, "cppCallBackMethod", "(Ljava/lang/String;I)V");const char *message = "cppA";int age = 10;(*env).CallVoidMethod(thiz, cppCallBackMethod, env->NewStringUTF(message), age);while (age < 50) {//睡眠1秒std::chrono::seconds duration(1); // 休眠一秒钟std::this_thread::sleep_for(duration);age = age + 10;(*env).CallVoidMethod(thiz, cppCallBackMethod, env->NewStringUTF(message), age);}return env->NewStringUTF(hello.c_str());
}
上面的代码运行也是一样的效果。
"->“和”(*env)."有啥区别?因为不是很熟悉,还还说不清。
上面不同写法调用方法的参数是有区别的,其实就是函数api的参数要求不同,具体可以看到jni.h的源码。
三、其他
1、C++到Java 相关api函数介绍
上面示例中使用用到的api:
(1)jobject m_object = env->NewGlobalRef(thiz);//创建对象的本地变量
(2)jclass mainActivityCls=env->FindClass("com/demo/jnicallback/MainActivity");//获取类对象(3)jmethodID cppCallBackMethod = env->GetMethodID(mainActivityCls, "cppCallBackMethod", "(Ljava/lang/String;I)V");(4)env->CallVoidMethod(m_object, cppCallBackMethod, env->NewStringUTF(message), age);
上面(1)和(2)是没什么研究价值的,NewGlobalRef和FindClass都是固定的写法。
(3)和(4)的不用方法的调用区别就比较大了,使用不同的api函数还可以修改Java的变量属性。
静态方法和动态方法调用的api函数也不一样,有返回值的方法和没有返回值的方法调用的api函数也是不一样的。
并且Java方法或者变量即使是private修饰的也不影响cpp调用过去,因为反射是不受修饰符影响的。
第3步里面的签名字符串“(Ljava/lang/String;I)V”,表示的是Java的方法和返回值的签名,唯一性;
这里面的签名字符串都是根据Java方法和方法的参数进行变化的。
下面对3、4步的代码相关知识做展开介绍。
2、调用获取不同方法和变量的api
方法、变量修饰类型表格
| 函数描述 | 描述 | |
|---|---|---|
| GetFieldID | 得到一个实例的域的ID | |
| GetStaticFieldID | 得到一个静态的域的ID | |
| GetMethodID | 得到一个实例的方法的ID | |
| GetStaticMethodID | 得到一个静态方法的ID |
上面Jni.cpp调用Java代码已经用到部分api方法,并且从字面含义也是比较容易里面这个表格的api的具体作用。
这个表格的用于就是为了获取到方法的修饰类型,比如方法,静态方法,变量,静态变量。
毕竟不同的修饰类型,在编译过程是有差异的。所以要区分。
3、Java签名类型字符串 常用的数据类型及对应字符:
上面示例中的"(Ljava/lang/String;I)V");字符串都是根据Java的方法通过下面这个表格转换来的。
| Java 类型 | Jni中表示的符号 | 备注 |
|---|---|---|
| boolean | Z | 不是类型首字母大写 |
| byte | B | |
| char | C | |
| short | S | |
| int | I | |
| long | L | |
| float | F | |
| double | D | |
| void | V | |
| objects对象 | Lfully-qualified-class-name;L全类名; | 记得最后是有分号的 |
| Arrays数组 | [array-type [数组类型 | |
| methods方法 | (argument-types)return-type(参数类型)返回类型 |
这个表格是有有啥用?就更多人懵逼了。
其实这些类型符号表示的是Java方法或者属性的一个签名,唯一性,目前就是为了让Jni.cpp调用到Java代码。
举个例子就很容易清楚了:
//XXX.Javaint age;String name;public int add(int number1,int number2){System.out.println("c/C++居然调用了我");return number1+number2;}//jni.cpp 修改Java属性值和调用Java方法示例//获取类对象
jclass mainActivityCls=env->FindClass("com/zmw/jnitest/MainActivity");//获取属性的fieldId,--》这里就用到了签名类型
jfieldID ageFid = env->GetFieldID(mainActivityCls,"age","I");
jfieldID nameFid=env->GetFieldID(mainActivityCls, "name", "Ljava/lang/String;");
//获取属性值
jint age = env->GetIntField(mainActivityThis,ageFid);
jstring name = (jstring)env->GetObjectField(thiz,nameFid);//此处有编码转换问题未解决//修改属性值,C++中修改变量值后,Java重新获取打印发现是修改过的
env->SetIntField(mainActivityThis, ageFid , 11);
env->SetObjectField(thiz, nameFid,Stringvalue);//获取方法的methodId,--》这里就用到了签名类型
jmethodID addMid=env->GetMethodID(mainActivityCls, "add", "(II)I");
int result=env->CallIntMethod(mainActivityThis, addMid, 1, 1); //这里就能获取到2的值。
仔细看一下上面的代码,就大致能理解这个签名表格的具体作用:为了找到Java方法的参数和返回值的形式。
Java签名类型小结:
(1)基础类型签名那些转换都是很容易记住的,基础类型中,特别留意一下boolean类型 是 Z 就行
(2)对象Object类型的转换是:L+全包名(包名直接用 /间隔)+类名+分号
(3)数组类型签名转换:[数组类型,比如[I,表示Java的 int[](4)方法签名的转换:(参数类型)返回类型,中间多个参数类型依此填写就行,
比如:Jni中的代码:env->GetMethodID("add", "(IILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;")
如果不清楚上面的表格转换,看起来就头大,特别是那些有三四个以上参数的情况,但是学习过后就不难了,
查看表格对应关系可以知道,Java中的对应方法是:public String add(int a,int b,String c,String d)
其实就是先看括号后面的返回值,然后再一个个确定括号内的形参变量
共勉: 这短短的一生,我们最终都会过去,你不妨大胆一些,爱一个人、攀一座山、追一个梦
相关文章:
Android JNI复杂用法,回调,C++中调用Java方法
Android JNI复杂用法,回调,C中调用Java方法 一、前言 Android JNI的 普通用法估计很多人都会,但是C中调用Java方法很多人不熟悉,并且网上很多介绍都是片段的。 虽然C/C调用Java不常用,但是掌握多一点还是有好处的。…...
C++从零开始的打怪升级之路(day41)
这是关于一个普通双非本科大一学生的C的学习记录贴 在此前,我学了一点点C语言还有简单的数据结构,如果有小伙伴想和我一起学习的,可以私信我交流分享学习资料 那么开启正题 今天分享的是关于继承的知识点 1.派生类的默认成员函数 首先我…...
uni-app app实现web-view H5图片长按下载
问题和使用场景描述: uniapp app web-view中图片无法长按保存,IOS下是正常的,但是Android下长按无反应 解决方案: 下载mui.min.js,放到项目中的static下(下载见最上面的压缩包) 在static目录下新建script.js mui.…...
全量知识系统问题及SmartChat给出的答复 之5
Q15. 支持前端(知识表征)的自然语言能力 需要一个 元语言注释工具 以及两个库(叙词库和语料库)和主题词表。请 1)设计 两个库和主题词表的结构 ,2)分别设计它们的接口,3)通过调用它们…...
DolphinScheduler——工作流实例的生命周期
目录 一、DolphinScheduler架构原理 1.1 系统架构图 1.2 DolphinScheduler核心概念 1.2 创建工作流 1.2.1 如何触发一个工作流实例 1.2.2 任务调度链路监控 1.2.3 Workflow-DAG解析 DAG解析 Dispatch分发流程 Master和Worker的交互过程 1.3 任务运行状态 该篇文章主…...
阻塞和非阻塞网络io有什么区别,分别有哪些应用场景?
阻塞(Blocking)和非阻塞(Non-blocking)网络I/O是两种不同的I/O模型,它们在处理I/O操作时的行为和特点有所不同。 阻塞式网络I/O(Blocking I/O): 在阻塞式网络I/O中,当应…...
面试数据库篇(mysql)- 12分库分表
拆分策略 垂直分库 垂直分库:以表为依据,根据业务将不同表拆分到不同库中。 特点: 按业务对数据分级管理、维护、监控、扩展在高并发下,提高磁盘IO和数据量连接数垂直分表:以字段为依据,根据字段属性将不同字段拆分到不同表中。 特点: 1,冷热数据分离 2,减少IO过渡争…...
LaTeX中的多行数学公式
目录 参考链接 一、gather以及gather*环境编排公式 1、 gather环境 2、 gather*环境 3、 阻止编号 二、align以及align*环境设定公式对齐方式 1、align环境 2、align*环境 三、split环境实现一个公式多行排版 四、cases环境实现分段函数 参考链接 LaTeX中的多行数学…...
绕过5秒盾Cloudflare和DDoS-GUARD
绕过5秒盾Cloudflare和DDoS-GUARD 5秒盾的特点免费版5秒盾的绕过方法付费版5秒盾的绕过方法 5秒盾的特点 <title>Just a moment...</title>例如: <!DOCTYPE html><html lang"en-US"><head><title>Just a moment...</title&…...
react 原理揭秘
1.目标 A. 能够知道setState()更新数据是异步的 B. 能够知道JSX语法的转化过程 C. 能够说出React组件的更新机制 D. 能够对组件进行性能优化 E. 能够说出虚拟DOM和Diff算法 2.目录 A. setState()的说明 B. JSX语法的转化过程 C. 组件更新机制 D. 组件性能优化 E. 虚拟DOM和D…...
el-table实现转置表格
vue版本:vue2.6.10 elementui版本:2.15.14 实现效果:el-table实现行列互换 代码: <template><div class"app-container"><span>原始数据</span><el-table:data"datas"border>…...
(3)(3.1) FlightDeck FrSky发射器应用程序
文章目录 前言 1 概述 2 Turnkey Packages 3 参数说明 前言 Craft and Theory 的 FlightDeck 可让你轻松查看飞行模式、高度、速度、姿态和关键系统警报,包括故障保护和电池错误,如电池不平衡警告和发射机低电量警报。 1 概述 Craft and Theory 的…...
【Unity】导入IAP插件后依赖冲突问题 com.android.billingclient冲突
【Unity】Attribute meta-data#com.google.android.play.billingclient.version 多版本库冲突_unity billingclient-CSDN博客 打开mainTemplate.gradle 找到dependencies { } 在里面末尾加上如下: configurations.all {exclude group: com.android.billingclien…...
docker 转为docker-compose(composerize 命令)
可以使用Composerize将Docker命令转换为Docker Compose文件。 例如:将docker run命令转换为Docker Compose格式,只需用Composerize运行它,如下所示: composerize docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/…...
【Golang切片】
切片 切片的引入内存分析切片的定义切片的遍历切片注意事项 切片的引入 【1】切片(slice)是golang中一种特有的数据类型 【2】数组有特定的用处,但是却有一些呆板(数组长度固定不可变),所以在Go语言的代码…...
React-router的创建和第一个组件
需要先学react框架 首先:找到一个文件夹,在文件夹出打开cmd窗口,输入如下图的口令 npx create-react-app demo 然后等待安装 安装完成 接下来进入创建的demo实例 cd demo 然后可以用如下方式打开vscode code . 注意:不要忽略点号与…...
计算机设计大赛 深度学习猫狗分类 - python opencv cnn
文章目录 0 前言1 课题背景2 使用CNN进行猫狗分类3 数据集处理4 神经网络的编写5 Tensorflow计算图的构建6 模型的训练和测试7 预测效果8 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 **基于深度学习猫狗分类 ** 该项目较为新颖&a…...
Linux服务器磁盘及内存用量监控Python脚本(推送钉钉群通知)
文章目录 Python 脚本钉钉推送通知定时任务 Python 脚本 # -*- coding: utf-8 -*- import subprocessdef get_disk_usage():# 执行 df 命令获取磁盘使用情况df_process subprocess.Popen([df, -h, /], stdoutsubprocess.PIPE)output, _ df_process.communicate()output out…...
Android13 Audio框架
一、Android 13音频代码结构 1、framework: android/frameworks/base 1.AudioManager.java :音频管理器,音量调节、音量UI、设置和获取参数等控制流的对外API 2.AudioService.java :音频系统服务(java层),…...
kafka消费者接收不到消息
背景: 对kafka消息进行监听,生产者发了消息,但是消费端没有接到消息,监听代码 消费端,kafka配置 spring.kafka.bootstrap-serverskafka.cestc.dmp:9591 spring.kafka.properties.sasl.jaas.configorg.apache.kafka.…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
uniapp 小程序 学习(一)
利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 :开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置,将微信开发者工具放入到Hbuilder中, 打开后出现 如下 bug 解…...
