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. 基础类型转换

JNIJava基础类型可以直接进行转换
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基础类型相对应

JavaNative
booleanjboolean
bytejbyte
charjchar
shortjshort
intjint
longjlong
floatjfloat
doublejdouble
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 ReferenceNative
All objectsjobject
java.lang.Classjclass
java.lang.Stringjstring
Object[]jobjectArray
boolean[]jbooleanArray
byte[]jbyteArray
java.lang.Throwablejthrowable
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实践基础

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

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

相关文章

正点原子嵌入式linux驱动开发——Linux 多点电容触摸屏

随着智能手机的发展&#xff0c;电容触摸屏也得到了飞速的发展。相比电阻触摸屏&#xff0c;电容触摸屏有很多的优势&#xff0c;比如支持多点触控、不需要按压&#xff0c;只需要轻轻触摸就有反应。ALIENTEK的三款RGB LCD屏幕都支持多点电容触摸&#xff0c;本章就以ATK7016这…

制作电子版宣传画册,抢占线上消费市场

随着互联网和移动设备的普及&#xff0c;企业在如今如此激烈的市场竞争中&#xff0c;怎样才能提高自身的宣传力度&#xff0c;进而让企业在市场中占一席之地呢&#xff1f;现在大多数企业都青睐于借助电子画册的形式来实现。 如何制作电子版宣传画册&#xff0c;抢占市场消费资…

基于混合蛙跳算法的无人机航迹规划-附代码

基于混合蛙跳算法的无人机航迹规划 文章目录 基于混合蛙跳算法的无人机航迹规划1.混合蛙跳搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用混合蛙跳算法来优化无人机航迹规划。 …

Spring Cloud Sentinel整合Nacos实现配置持久化

sentinel配置相关配置后无法持久化&#xff0c;服务重启之后就没了&#xff0c;所以整合nacos&#xff0c;在nacos服务持久化&#xff0c;sentinel实时与nacos通信获取相关配置。 使用上一章节Feign消费者服务实现整合。 版本信息&#xff1a; nacos:1.4.1 Sentinel 控制台 …

AI直播换脸——DeepFaceLab 3.0模型训练与微调

前言 DeepFaceLab是一种基于深度学习的人脸合成和转换工具。它使用了深度神经网络来分析和修改图像中的人脸部分&#xff0c;可以实现将一个人的脸部特征应用到另一个人的照片上&#xff0c;或者进行面部表情、年龄、性别等特征的变换。 DeepFaceLab具备一系列核心功能&#x…

Redis的淘汰策略

Redis是一种基于内存的高性能键值存储数据库&#xff0c;由于内存资源有限&#xff0c;当Redis中的数据量增大时&#xff0c;会导致内存占用过多&#xff0c;可能会影响系统的性能和稳定性。为了解决这个问题&#xff0c;Redis提供了一些淘汰策略来自动清理不再使用的key&#…

基于 ARM+FPGA+AD的高精度数据采集系统设计

随着图像处理 、 工业控制 、 无线通信等领域的飞速发 展 &#xff0c; 对数据采集系统的速度 、 精度等性能要求也越来越高 。 这些要求都对数据采集系统的设计和实现提出了新的挑 战 。 目前数据采集系统的设计方案通常分为以下几类 &#xff1a; &#xff11; &#xff0…

分类预测 | Matlab实现KOA-CNN-BiGRU-selfAttention多特征分类预测(自注意力机制)

分类预测 | Matlab实现KOA-CNN-BiGRU-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09; 目录 分类预测 | Matlab实现KOA-CNN-BiGRU-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09;分类效果基本描述程序设计参考资料 分类效果 基本描述 1.M…

【数据结构】数组和字符串(五):特殊矩阵的压缩存储:稀疏矩阵——压缩稀疏行(CSR)

文章目录 4.2.1 矩阵的数组表示4.2.2 特殊矩阵的压缩存储a. 对角矩阵的压缩存储b~c. 三角、对称矩阵的压缩存储d. 稀疏矩阵的压缩存储——三元组表e. 压缩稀疏行&#xff08;Compressed Sparse Row&#xff0c;CSR&#xff09;矩阵结构体创建CSR矩阵元素设置初始化打印矩阵销毁…

Linux多核CPU启动内核调试(详细)总结

目录 一、综述二、调试流程简介2.1 大体流程2.2 spin-table简介 三、uboot和内核配置3.1 uboot配置3.2 timer配置3.3 GIC中断配置3.4 驱动确认3.5 SMP配置3.6 内核config配置 四、其他相关链接1、[交叉编译linux内核总结](https://blog.csdn.net/Luckiers/article/details/1245…

python:使用Scikit-image对遥感影像进行傅里叶变换特征提取(fourier)

作者:CSDN @ _养乐多_ 在本博客中,我们将介绍如何使用Scikit-Image来进行傅里叶变换特征提取(fourier),并且提供一个示例代码,演示了如何在单波段遥感图像上应用这些方法。 傅里叶变换特征提取是一种数学工具,用于将图像中的细节、纹理和边缘信息以不同频率的方式呈现…

电脑监控软件哪些比较好用

电脑监控软件在当今信息化时代越来越受到人们的关注&#xff0c;它们可以用于保护公司的商业机密&#xff0c;防止员工在工作中做一些不恰当的事情&#xff0c;以及在家庭中监控孩子的上网行为等。 本文将介绍一些比较好用的电脑监控软件&#xff1a; 一、域之盾软件 这款软件…

npm更新包时This operation requires a one-time password.

[访问我的npm包](mhfwork/yt-ui - npm) 更新npm包时出现 This operation requires a one-time password.是因为需要认证 解决办法 1. 点击红线处的链接 2. 进入npm官网获取指定秘钥 3. 再次填入 one-time password 即可

Linux进程地址空间

文章目录 背景进程地址空间分页和虚拟地址空间 写时拷贝 背景 研究背景&#xff1a;我们在之前通过fork函数创建子进程的时候&#xff0c;我们发现fork的返回值有两个&#xff0c;且值不相同但地址确实相同的&#xff0c;我们知道在物理空间上这种情况是不可能存在的&#xff…

SQL中:语法总结(group by,having ,distinct,top,order by,like等等)

语法总结&#xff1a;group by&#xff0c;distinct ...... 1.分组group by、条件havinggroup byhaving 2.聚集函数count 3.order by4.对表中数据的操作&#xff1a;增insert、删delete、改update增insert删delete改update 5.对表中数据的操作&#xff1a;查select嵌套查询不相…

如何防范AI等技术带来的诈骗风险?从技术、法律、教育等多方面入手

文章目录 前言什么是AI诈骗案例案例一案例二 AI诈骗的特点如何预防和应对AI诈骗建议后记 前言 互联网是一把双刃剑&#xff0c;这是我们常说的一个问题。 随着人工智能技术的快速发展&#xff0c;AI诈骗成为当今社会面临的新兴威胁。不法分子利用人工智能技术&#xff0c;以更…

8.自定义组件布局和详解Context上下文

pages/index.vue layout布局运行在服务端 1、在项目的目录下新建layout文件夹&#xff0c;并新建一个blog.vue布局文件 2、在页面中的layout函数里&#xff0c;返回刚才新建布局文件的名字blog就可以使用了 export default {...layout (context) {console.log(context)retu…

在3分钟内使用AI-Chat生成精美PPT(附AI工具)

前言 在人工智能的大趋势下&#xff0c;AI-Chat是一款令人惊叹的技术。它用强大的自然语言处理技术帮助我们快速生成PPT&#xff0c;提高工作效率。本文将介绍使用ChatAI-Chat生成PPT的方法&#xff0c;以及使用Mindshow转换为炫酷的演示文稿。让技术为我们节省时间&#xff0c…

Ubuntu中使用yum命令出现错误提示:Command ‘yum‘ not found, did you mean:

Ubuntu中使用yum命令出现错误提示:Command ‘yum’ not found, did you mean: command ‘gum’ from snap gum (0.12.0) command ‘num’ from deb quickcal (2.4-1) command ‘yum4’ from deb nextgen-yum4 (4.5.2-6) command ‘uum’ from deb freewnn-jserver (1.1.1~a021…

Stable Diffusion系列(一):古早显卡上最新版 WebUI 安装及简单操作

文章目录 Stable Diffusion安装AnimateDiff插件适配sdxl模型适配 Stable Diffusion使用插件安装界面设置基础文生图加入lora的文生图 Stable Diffusion安装 我的情况比较特殊&#xff0c;显卡版本太老&#xff0c;最高也就支持cuda10.2&#xff0c;因此只能安装pytorch1.12.1&…