Android JNI/NDK 入门从一到二
1. 前言
最基础的创建JNI接口的操作,可以直接看这篇文章 : 第一个Android JNI工程,
本文会基于掌握创建JNI接口的操作的基础之上,来入门JNI/NDK。
2. 在JNI中打印日志
2.1 添加log模块
记得CMake中有log模块,不然编译不过
target_link_libraries(#...省略androidlog)
2.2 添加头文件
#include <android/log.h>
2.3 定义Log方法
#define LOG_TAG "CPPLOG"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG , __VA_ARGS__) // 定义LOGD类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG , __VA_ARGS__) // 定义LOGE类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG , __VA_ARGS__) // 定义LOGE类型
2.4 进行调用
LOGD("java int value is %p", value);
3. 基础类型转换
JNI和Java基础类型可以直接进行转换
在jni.h中我们可以看到JNI的基础类型有这些,比如jint其实就是对应C++中的int32_t类型
/* Primitive types that match up with Java equivalents. */
typedef uint8_t jboolean; /* unsigned 8 bits */
typedef int8_t jbyte; /* signed 8 bits */
typedef uint16_t jchar; /* unsigned 16 bits */
typedef int16_t jshort; /* signed 16 bits */
typedef int32_t jint; /* signed 32 bits */
typedef int64_t jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 */
在C++中,_t是一种命名约定,表示某个类型。通常在命名中使用_t作为类型的后缀,以便区分该名称是一个类型而不是其他实体(例如变量或函数)。
我把它整理成了一个表格,Java基础类型和JNI基础类型相对应
| Java | Native |
|---|---|
| boolean | jboolean |
| byte | jbyte |
| char | jchar |
| short | jshort |
| int | jint |
| long | jlong |
| float | jfloat |
| double | jdouble |
3.1 编写JNI方法
在Java类中编写JNI方法
external fun callNativeInt(value:Int) : Intexternal fun callNativeByte(value:Byte) : Byteexternal fun callNativeChar(value:Char) : Charexternal fun callNativeLong(value:Long) : Longexternal fun callNativeFloat(value:Float) : Floatexternal fun callNativeDouble(value:Double) : Double
3.2 C++中编写对应的方法
extern "C"
JNIEXPORT jint JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeInt(JNIEnv *env, jobject thiz, jint value) {LOGD("value:%d", value);return value + 1;
}
extern "C"
JNIEXPORT jbyte JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeByte(JNIEnv *env, jobject thiz, jbyte value) {LOGD("value:%d", value);return value + 1;
}
extern "C"
JNIEXPORT jchar JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeChar(JNIEnv *env, jobject thiz, jchar value) {LOGD("value:%d", value);return value + 1;
}
extern "C"
JNIEXPORT jlong JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeLong(JNIEnv *env, jobject thiz, jlong value) {LOGD("value:%d", value);return value + 1;
}
extern "C"
JNIEXPORT jfloat JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeFloat(JNIEnv *env, jobject thiz, jfloat value) {LOGD("value:%f", value);return value + 1.0;
}
extern "C"
JNIEXPORT jdouble JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeDouble(JNIEnv *env, jobject thiz,jdouble value) {LOGD("value:%f", value);return value + 1.0;
}
3.3. 进行调用
Log.i(TAG, "result:${nativeLib.callNativeInt(1)}")
Log.i(TAG, "result:${nativeLib.callNativeByte(2)}")
Log.i(TAG, "result:${nativeLib.callNativeChar('c')}")
Log.i(TAG, "result:${nativeLib.callNativeLong(4)}")
Log.i(TAG, "result:${nativeLib.callNativeFloat(5F)}")
Log.i(TAG, "result:${nativeLib.callNativeDouble(6.0)}")
3.4 运行项目
打印日志如下
10:16:36.815 D value:1
10:16:36.815 I result:2
10:16:36.815 D value:2
10:16:36.815 I result:3
10:16:36.815 D value:99
10:16:36.815 I result:d
10:16:36.815 D value:4
10:16:36.815 I result:5
10:16:36.815 D value:5.000000
10:16:36.815 I result:6.0
10:16:36.816 D value:6.000000
10:16:36.816 I result:7.0
4. 字符串
Java字符串转成Native的字符串,并不能直接做转换,需要调用env->GetStringUTFChars(),
对应的,需要调用env->ReleaseStringUTFChars()来释放资源。
默认情况下,Java都是UTF编码,如果不是UTF编码,则需要调用
env->GetStringChars()
4.1 Java/Native字符串转换
external fun callNativeString(value:String) : String
extern "C"
JNIEXPORT jstring JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeString(JNIEnv *env, jobject thiz,jstring value) {//Java字符串转成Native的字符串,并不能直接做转换const char *str = env->GetStringUTFChars(value, NULL); //Java的字符串是UTF编码的//env->GetStringChars(); //如果不是UTF编码,就用这个LOGD("str:%s", str);env->ReleaseStringUTFChars(value, str);jstring result = env->NewStringUTF("hello world!");return result;
}
进行调用
Log.i(TAG, "result:${nativeLib.callNativeString("你好呀")}")
nativeLib.stringMethod("hello world!")
执行结果
10:45:45.849 D str:你好呀
10:45:45.849 I result:hello world!
4.2 C++ 字符串的使用
定义JNI接口
external fun stringMethod(value:String)
实现C++方法
extern "C"
JNIEXPORT void JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_stringMethod(JNIEnv *env, jobject thiz, jstring value) {const char *str = env->GetStringUTFChars(value, 0);int length = env->GetStringLength(value);LOGD("length:%d", length);char buf[256];env->GetStringUTFRegion(value, 0, length, buf); //拷贝字符串数据到char[]中LOGD("text:%s", buf);env->ReleaseStringUTFChars(value, str);
}
进行调用
Log.i(TAG, "result:${nativeLib.callNativeString("你好呀")}")
nativeLib.stringMethod("hello world!")
执行结果
10:45:45.849 D length:12
10:45:45.849 D text:hello world!
5. 引用类型的使用
这里列出了Java引用类型和JNI应用类型的对应关系。
值得注意的是,不是所有的Java引用类型都有对应的JNI的引用类型。
比如Java中的字符串数组String[],就没有相对应的JNI的引用类型,这种情况下,都会统一归类为jobject。
| Java Reference | Native |
|---|---|
| All objects | jobject |
| java.lang.Class | jclass |
| java.lang.String | jstring |
| Object[] | jobjectArray |
| boolean[] | jbooleanArray |
| byte[] | jbyteArray |
| java.lang.Throwable | jthrowable |
| char[] | jcharArray |
| short[] | jshortArray |
| int[] | jintArray |
| long[] | jlongArray |
| float[] | jfloatArray |
| double[] | jdoubleArray |
5.1 传递字符串数据
Java层传递一个字符串数组,然后C++层接收到后,获取这个字符串数组的第一个字符串,并打印出来。
定义JNI接口
external fun callNativeStringArray(array:Array<String>)
实现C++方法,这里因为是字符串数组,JNI中没有相对应的类型,所以需要先通过env->GetObjectArrayElement()获取到Object数组中的第一个索引的Object,再将其强转为jstring类型。
如果是JNI有对应类型的,按直接调用相关API就可以了,比如env->GetIntArrayElements()、env->GetFloatArrayElements()
extern "C"
JNIEXPORT void JNICALL
Java_com_heiko_myopencvtest2023_NativeLib_callNativeStringArray(JNIEnv *env, jobject thiz,jobjectArray array) {int len = env->GetArrayLength(array);LOGD("len:%d",len);//env->GetIntArrayElements() //获取Int数组//env->GetFloatArrayElements() //获得Float数组//env->GetObjectArrayElement() //获得JNI数组jstring result = static_cast<jstring>(env->GetObjectArrayElement(array, 0)); //获取index为0的值const char * str = env->GetStringUTFChars(result,NULL);LOGD("text[0]:%s",str);env->ReleaseStringUTFChars(result,str);
}
static_cast是进行类型的强转
进行调用
val array = arrayOf("ABC", "DEF", "GHI", "JKL", "MNO")
nativeLib.callNativeStringArray(array)
执行结果
13:27:06.865 D len:5
13:27:06.865 D text[0]:ABC
6. 传递Bitmap
这里我们以镜像Bitmap图片为例,传递Bitmap图片到JNI层,然后进行镜像操作,并将镜像后的Bitmap图片返回给Java层
6.1 获取Bitamp的信息
调用AndroidBitmap_getInfo(),用来获取Bitmap的信息。
AndroidBitmapInfo bitmapInfo;
AndroidBitmap_getInfo(env, bitmap, &bitmapInfo);
6.2 获取Bitmap像素内容
调用AndroidBitmap_lockPixels(),用来获取Bitmap的像素内容。
同时,记得需要调用AndroidBitmap_unlockPixels()来释放资源,这两个API是配对使用的。
//拿到像素内容
void *bitmapPixels;
AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels);//释放资源
AndroidBitmap_unlockPixels(env, bitmap);
6.3 JNI中创建Bitamp
直接复制这个封装好的方法,进行调用就好
jobject generateBitmap(JNIEnv *env, uint32_t width, uint32_t height) {// 获取Bitmap类引用jclass bitmapCls = env->FindClass("android/graphics/Bitmap");// 获取Bitmap构造方法的引用jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls, "createBitmap","(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");jstring configName = env->NewStringUTF("ARGB_8888");jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config");jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(bitmapConfigClass, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass,valueOfBitmapConfigFunction, configName);jobject newBitmap = env->CallStaticObjectMethod(bitmapCls, createBitmapFunction, width, height,bitmapConfig);return newBitmap;
}
6.4 实现Bitmap镜像操作
定义JNI
external fun mirrorBitmap(bitmap: Bitmap) : Bitmap
实现C++代码
extern "C"
JNIEXPORT jobject JNICALL
Java_com_heiko_myncnnlib_NcnnNativeLib_mirrorBitmap(JNIEnv *env, jobject thiz, jobject bitmap) {AndroidBitmapInfo bitmapInfo;AndroidBitmap_getInfo(env, bitmap, &bitmapInfo);__android_log_print(ANDROID_LOG_DEBUG, "jniBitmap", "width:%d,height:%d", bitmapInfo.width,bitmapInfo.height);//拿到像素内容void *bitmapPixels;AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels);uint32_t newWidth = bitmapInfo.width;uint32_t newHeight = bitmapInfo.height;uint32_t *newBitmapPixels = new uint32_t[newWidth * newHeight];int index = 0;//遍历Bitmap像素,将左右的像素进行互换 (镜像操作)for (int y = 0; y < newHeight; y++) {for (int x = newWidth - 1; x >= 0; x--) {uint32_t pixel = ((uint32_t *) bitmapPixels)[index++];newBitmapPixels[newWidth * y + x] = pixel;}}AndroidBitmap_unlockPixels(env, bitmap);//生成新的Bitmapjobject newBitmap = generateBitmap(env, newWidth, newHeight);void *resultBitmapPixels;AndroidBitmap_lockPixels(env, newBitmap, &resultBitmapPixels);//拷贝memcpy((uint32_t *)resultBitmapPixels, newBitmapPixels, sizeof(uint32_t) * newWidth * newHeight);AndroidBitmap_unlockPixels(env,newBitmap);delete [] newBitmapPixels;return newBitmap;
}
进行调用
var bitmap = BitmapFactory.decodeResource(resources,R.drawable.img_test)
binding.img1.setImageBitmap(bitmap)binding.btnMirrorImage.setOnClickListener {bitmap = nativeLib.mirrorBitmap(bitmap)binding.img1.setImageBitmap(bitmap)
}
进行程序,点击Button,可以发现图片执行了镜像操作。

7. 其他
7.1 CMake
关于CMake可以看我的另一篇博客 : Android NDK CMakeLists.txt 常用命令说明
7.2 参考
感谢 Android CMake以及NDK实践基础
相关文章:
Android JNI/NDK 入门从一到二
1. 前言 最基础的创建JNI接口的操作,可以直接看这篇文章 : 第一个Android JNI工程, 本文会基于掌握创建JNI接口的操作的基础之上,来入门JNI/NDK。 2. 在JNI中打印日志 2.1 添加log模块 记得CMake中有log模块,不然编译不过 ta…...
吃瓜教程3|决策树
ID3算法 假定当前样本集合D中第k类样本所占比例为pk,则样本集合D的信息熵定义为 信息增益 C4.5算法 ID3算法存在一个问题,就是偏向于取值数目较多的属性,因此C4.5算法使用了“增益率”(gain ratio)来选择划分属性 CA…...
springboot动态数据源【非伪数据源】
说明:本文章的数据源不是在配置文件中配置两个或多个数据源,在业务方面对这些数据源来回切换,本文章中的数据源是可以动态添加,修改,切换的,废话不多说。 先看工程图: 1.pom.xml文件 <?x…...
如何改善设备综合效率(OEE)并提高工厂的生产力
在现代制造业中,提高设备综合效率(Overall Equipment Efficiency,OEE)是企业追求高效生产和优化生产能力的重要目标之一。OEE是一个关键的绩效指标,可以帮助企业评估设备的利用效率、生产效率和质量水平。本文将从三个…...
一文接入Android阿里Sophix热更新
最近公司项目渐趋成熟,已经不需要经常更新版本,并且更新版本对客户的影响特别大,但是日常维护难免需要更新代码,因此热修复的技术,就比较迫切了。 经过一段时间的对比,我们最终决定使用阿里的Sophix方案&am…...
【高阶数据结构】并查集和图
目录 1.数据结构--并查集 2.数据结构--图 1.图的基础概念 2.图的简单实现 2.1.邻接矩阵的图实现 2.2.邻接表的图实现 2.3.图的DFS和BFS 2.4.最小生成树 2.4.1.Kruskal(克鲁斯卡尔算法) 2.4.2.Prim(普里姆算法) 2.5.最短路径 2.5.1.Dijkstra(…...
Git 提交时提示 GPG 签名错误
本来应该一切都是正常的,但今天提交的时候提示 GPG 签名错误。 错误的信息就是 GPG 签名失败。 gpg: skipped "942395299055675C": No secret key gpg: signing failed: No secret key error: gpg failed to sign the data fatal: failed to write commi…...
vite+vue3实现 tomcat 的本地部署
背景: 很多开发小伙伴在本地开发完前端项目后,碍于服务端环境配置麻烦,想先试试在本地部署,已开发好的前端项目,由于很多文章都是文字性描述,不太直观,为了给大多数新手提供一个教程,…...
docker+playwright
windows10 docker playwright 难点在于windows下docker的安装,以及官方hub被墙的困难。 wsl2 wsl2 ubuntu docker git clone https://gitee.com/lineuman/lcs_playwright.git npm install npx playwright test docker端口怎么映射到主机上面? 设置重…...
php框架路由实现
在PHP中也有很多框架(如Laravel、CodeIgniter)提供了路由功能。下面是一个简单的PHP路由实现原理和示例代码: 路由实现原理: 客户端发起请求,请求的URL会被传递给Web服务器。Web服务器将请求传递给PHP解释器ÿ…...
在CentOS 7中手工打造和运行xml文件配置的Servlet,然后使用curl、浏览器、telnet等三种工具各自测试
下载Openjdk并配置环境变量 https://jdk.java.net/java-se-ri/11-MR2是官网下载Openjdk 11的地方。 sudo wget https://download.java.net/openjdk/jdk11.0.0.1/ri/openjdk-11.0.0.1_linux-x64_bin.tar.gz下载openjdk 11。 sudo mkdir -p /usr/openjdk11创建目录ÿ…...
单例模式.
目录 ♫什么是单例模式 ♫饿汉式单例模式 ♫懒汉式单例模式 ♫单例模式的线程安全问题 ♪原子性 ♪内存可见性与指令重排序 ♫什么是单例模式 单例模式是一种设计模式,通过巧用Java的现有语法,实现一个只能被创建一个实例的类,并提供一个全…...
2023年MathorCup高校数学建模挑战赛大数据挑战赛赛题浅析
比赛时长为期7天的妈杯大数据挑战赛如期开赛,为了帮助大家更好的选题,首先给大家带来赛题浅析,为了方便大家更好的选题。 赛道 A:基于计算机视觉的坑洼道路检测和识别 A题,图像处理类题目。这种题目的难度数模独一档…...
c++小惊喜——stringstream
当需要读取一行字符串时,我们通常会有将这个字符串分开的想法 #include<iostream> #include<sstream> using namespace std;int main() {string str;getline(cin, str);stringstream ssin(str);string s[10];int cnt 0;while (ssin >> s[cnt]) …...
ubuntu 18.04 编译安装flexpart 10.4(2023年) —— 筑梦之路
2023年10月29日 环境说明 操作系统版本:ubuntu 18.04 python版本:3.6.9 gcc版本:7.5.0 编译安装路径:/usr/local cmake: 3.10.2 所需要的源码包我已经打包放到我的资源。 2021年1月份已经写过一篇Ubuntu 编译安装的帖子F…...
深度学习(生成式模型)——DDIM:Denoising Diffusion Implicit Models
文章目录 前言为什么DDPM的反向过程与前向过程步数绑定DDIM如何减少DDPM反向过程步数DDIM的优化目标DDIM的训练与测试 前言 上一篇博文介绍了DDIM的前身DDPM。DDPM的反向过程与前向过程步数一一对应,例如前向过程有1000步,那么反向过程也需要有1000步&a…...
HashMap的遍历方式 -- 好几次差点记不起来总结了一下
public class HashMapDemo {public static void main(String[] args) {// 创建一个HashMap并添加一些键值对Map<String, Integer> hashMap new HashMap<>();hashMap.put("Alice", 25);hashMap.put("Bob", 30);hashMap.put("Charlie"…...
PostgreSQL 两表关联更新sql
PostgreSQL两表关联更新SQL如下: UPDATE user SET username ft.name, age ft.age FROM userinfo WHERE user.id ft.id; user 要更新的表 userinfo数据来源表...
R2R 的一些小tip
批次间控制器(Run-to-run Controller),以应对高混合生产的挑战。将最优配方参数与各种工业特征相关联的模型是根据历史数据离线训练的。预测的最优配方参数在线用于调整工艺条件。 批次控制(R2R control)是一种先进的工艺控制技术,可在运行(如批次或晶圆…...
UML中类之间的六种主要关系
UML中类之间的六种主要关系: 继承(泛化)(Inheritance、Generalization), 实现(Realization),关联(Association),聚合(Aggregation),组…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
