4.2 Android NDK 基础概念

news2024/11/17 22:08:44

1 JavaVMJNIEnv

  JNI 定义了两个关键数据结构,JavaVMJNIEnv。这两者本质上都是指向函数表指针的指针。(在 C++ 版本中,它们是具有指向函数表的指针的类,以及指向该表的每个 JNI 函数的成员函数。)JavaVM提供了“调用接口”函数,允许您创建和销毁JavaVM。理论上,每个进程可以有多个JavaVM,但 Android 只允许一个。

  JNIEnv提供了大部分 JNI 功能。除了@CriticalNative方法外,您的原生函数都会收到JNIEnv作为第一个参数。

  JNIEnv用于线程本地存储。因此,您不能在线程之间共享JNIEnv。如果一段代码没有其他方法获取其JNIEnv,则应共享JavaVM,并使用GetEnv发现线程的JNIEnv。(假设它有一个)

  JNIEnvJavaVM的 C 声明与 C++ 声明不同。jni.h包含文件提供不同的typedef,具体取决于它是包含在 C 还是 C++ 中。因此,在两种语言都包含的头文件中包含JNIEnv参数是一个坏主意。(换句话说:如果你的头文件需要#ifdef __cplusplus,如果该头文件中的任何内容引用JNIEnv,你可能需要做一些额外的工作。)

2 jclassjmethodIDjfieldID

  如果要从原生代码访问对象的字段,可以执行以下操作:

  • 使用FindClass获取类的类对象引用
  • 使用GetFieldID获取字段的字段 ID
  • 使用适当的东西获取字段的内容,例如GetIntField

  同样,要调用一个方法,您首先会得到一个类对象引用,然后是一个方法 ID。这些 ID 通常只是指向内部运行时数据结构的指针。查找它们可能需要几个字符串比较,但一旦你有了它们,获取字段或调用方法的实际调用就非常快了。

  如果性能很重要,那么查找一次值并将结果缓存在原生代码中是有用的。因为每个进程只能有一个JavaVM,所以将这些数据存储在静态本地结构中是合理的。

  类引用、字段 ID 和方法 ID 保证有效,直到类被卸载。只有当与ClassLoader关联的所有类都可以被垃圾回收时,类才会被卸载,这在 Android 中很少见,但并非不可能。但是请注意,jclass是一个类引用,必须通过调用NewGlobalRef来保护它。

  如果你想在加载类时缓存 ID,并在卸载和重新加载类时自动重新缓存它们,初始化 ID 的正确方法是在相应的类中添加一段看起来像这样的代码:

    /*
     * We use a class initializer to allow the native code to cache some
     * field offsets. This native function looks up and caches interesting
     * class/field/method IDs. Throws on failure.
     */
    private static native void nativeInit();

    static {
        nativeInit();
    }

  在 C/C++ 代码中创建一个执行 ID 查找的nativeClassInit方法。代码将在类初始化时执行一次。如果类被卸载然后重新加载,它将再次执行。

3 局部和全局引用

  传递给原生方法的每个参数以及 JNI 函数返回的几乎每个对象都是“局部引用”。这意味着它在当前线程中当前原生方法的持续时间内有效。即使对象本身在原生方法返回后继续存在,引用也是无效的。

  这适用于jobject的所有子类,包括jclassjstringjarray。(启用扩展 JNI 检查时,运行时将警告您大多数引用错误使用。)

  获取非局部引用的唯一方法是通过函数NewGlobalRefNewWeakGlobalRef

  如果你想在更长的时间内保留一个引用,你必须使用“全局”引用。NewGlobalRef函数将局部引用作为参数并返回全局引用。在您调用DeleteGlobalRef之前,全局引用保证有效。

  此模式通常用于缓存从FindClass返回的jclass,例如:

jclass localClass = env->FindClass("MyClass");
jclass globalClass = reinterpret_cast<jclass>(env->NewGlobalRef(localClass));

  所有 JNI 方法都接受局部和全局引用作为参数。对同一对象的引用可能具有不同的值。例如,对同一对象连续调用NewGlobalRef的返回值可能不同。要查看两个引用是否引用同一个对象,必须使用IsSameObject函数。切勿在原生代码中使用==比较引用。

  这样做的一个后果是,您不能假设对象引用在原生代码中是恒定的或唯一的。表示对象的值可能因方法的一次调用而不同,并且两个不同的对象在连续调用时可能具有相同的值。不要将jobject值用作键。

  程序员被要求“不要过度分配”局部引用。实际上,这意味着,如果您正在创建大量的局部引用,也许是在运行一组对象时,您应该使用DeleteLocalRef手动释放它们,而不是让 JNI 为您完成。该实现只需要为 16 个局部引用保留插槽,因此如果您需要更多,您应该边走边删除,或者使用EnsureLocalCapacity/PushLocalFrame保留更多。

  请注意,jfieldIDjmethodID是不透明类型,不是对象引用,不应传递给NewGlobalRefGetStringUTFCharGetByteArrayElements等函数返回的原始数据指针也不是对象。(它们可以在线程之间传递,并且在匹配的Release调用之前有效。)

  一个不寻常的案例值得单独提及。如果使用AttachCurrentThread附加原生线程,则在线程分离之前,您正在运行的代码将永远不会自动释放局部引用。您创建的任何局部引用都必须手动删除。一般来说,任何在循环中创建局部引用的原生代码都可能需要手动删除。

  使用全局引用时要小心。全局引用可能是不可避免的,但它们很难调试,并可能导致难以诊断的内存(错误)行为。在其他条件相同的情况下,全局引用较少的解决方案可能更好。

4 原生库

  您可以使用标准System.loadLibrary从共享库加载原生代码。

  从静态类初始化器调用System.loadLibrary,参数是“未修饰”的库名称,因此要加载libfubar.so,您需要传入fubar

  如果你只有一个具有原生方法的类,那么在该类的静态初始化器中调用System.loadLibrary是有意义的。否则,您可能希望从Application进行调用,这样您就知道库总是加载的,并且总是提前加载。

  运行时可以通过两种方式找到您的原生方法。您可以使用RegisterNatives显式注册它们,也可以让运行时使用dlsym动态查找它们。RegisterNatives的优点是,您可以预先检查符号是否存在,此外,通过只导出JNI_OnLoad,您可以拥有更小、更快的共享库。让运行时发现您的函数的优点是,编写的代码稍微少一些。

  要使用RegisterNatives,请执行以下操作:

  • 提供JNIEXPORT jint JNI_OnLoad(JavaVM* vm,void* reserved)函数。
  • JNI_OnLoad中,使用RegisterNatives注册所有原生方法。
  • 使用版本脚本(首选)进行构建,或使用-fvisibility=hidden,以便仅从库中导出JNI_OnLoad。这会生成更快、更小的代码,并避免与加载到应用程序中的其他库发生潜在冲突(但如果应用程序在原生代码中崩溃,它会创建不太有用的堆栈跟踪)。

  静态初始化器应该如下所示:

static {
    System.loadLibrary("fubar");
}

  如果用 C++ 编写,JNI_OnLoad函数应该看起来像这样:

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }

    // Find your class. JNI_OnLoad is called from the correct class loader context for this to work.
    jclass c = env->FindClass("com/example/app/package/MyClass");
    if (c == nullptr) return JNI_ERR;

    // Register your class' native methods.
    static const JNINativeMethod methods[] = {
        {"nativeFoo", "()V", reinterpret_cast<void*>(nativeFoo)},
        {"nativeBar", "(Ljava/lang/String;I)Z", reinterpret_cast<void*>(nativeBar)},
    };
    int rc = env->RegisterNatives(c, methods, sizeof(methods)/sizeof(JNINativeMethod));
    if (rc != JNI_OK) return rc;

    return JNI_VERSION_1_6;
}

  要使用原生方法的“发现”,您需要以特定的方式命名它们。这意味着,如果一个方法签名是错误的,你只有在第一次实际调用该方法时才会知道。

  从JNI_OnLoad进行的任何FindClass调用都将解析用于加载共享库的类加载器上下文中的类。当从其他上下文调用时,FindClass使用与 Java 堆栈顶部的方法关联的类加载器,或者如果没有(因为调用来自刚刚附加的原生线程),则使用“系统”类加载器。系统类加载器不知道应用程序的类,因此您将无法在该上下文中使用FindClass查找自己的类。这使得JNI_OnLoad成为查找和缓存类的一个方便的地方:一旦你有了一个有效的jclass全局引用,你就可以从任何连接的线程中使用它。

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

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

相关文章

高光谱深度学习调研

综述 高光谱深度学习只有小综述&#xff0c;没有大综述。小综述里面场景分类、目标检测的综述比较多。 Wang C, Liu B, Liu L, et al. A review of deep learning used in the hyperspectral image analysis for agriculture[J]. Artificial Intelligence Review, 2021, 54(7)…

抖音热门素材去哪找?优质抖音视频素材网站推荐!

是不是和我一样&#xff0c;刷抖音刷到停不下来&#xff1f;越来越多的朋友希望在抖音上创作出爆款视频&#xff0c;但苦于没有好素材。今天就来推荐几个超级实用的抖音视频素材网站&#xff0c;让你的视频内容立刻变得高大上&#xff01;这篇满是干货&#xff0c;直接上重点&a…

人工智能之数学基础:数学在人工智能领域中的地位

人工智能&#xff08;AI&#xff09;是一种新兴的技术&#xff0c;它的目标是构建能够像人类一样思考、学习、推理和解决问题的智能机器。AI已经成为了许多行业的重要组成部分&#xff0c;包括医疗、金融、交通、教育等。而数学则是AI领域中不可或缺的基础学科。本文将阐述数学…

高翔【自动驾驶与机器人中的SLAM技术】学习笔记(十三)图优化SLAM的本质

一、直白解释slam与图优化的结合 我从b站上学习理解的这个概念。 视频的大概位置是1个小时以后&#xff0c;在第75min到80min之间。图优化SLAM是怎么一回事。 slam本身是有运动方程的&#xff0c;也就是运动状态递推方程&#xff0c;也就是预测过程。通过t1时刻&#xff0c…

STM32单片机设计防儿童人员误锁/滞留车内警报系统

目录 目录 前言 一、本设计主要实现哪些很“开门”功能&#xff1f; 二、电路设计原理图 1.电路图采用Altium Designer进行设计&#xff1a; 2.实物展示图片 三、程序源代码设计 四、获取资料内容 前言 近年来在车辆逐渐普及的情况下&#xff0c;由于家长的疏忽&#xff0c;将…

stm32——通用定时器时钟知识点

&#xff08;该图来自小破站 铁头山羊老师的stm32标准库教学&#xff09;

GPIO相关的寄存器(重要)

目录 一、GPIO相关寄存器概述 二、整体介绍 三、详细介绍 1、端口配置低寄存器&#xff08;GPIOx_CRL&#xff09;&#xff08;xA...E&#xff09; 2、端口配置高寄存器&#xff08;GPIOx_CRH&#xff09;&#xff08;xA...E&#xff09; 3、端口输入数据寄存器&#xff…

CSS基础也要进行模电实验

盒子阴影 圆角边框已经介绍过哩&#xff0c;现在先介绍一下盒子阴影的效果如何实现 CSS3中新增了盒子阴影&#xff0c;可以使用box-shadow属性为盒子添加阴影 这是固定的语法&#xff1a; text-shadow: h-shadow v-shadow blur color; 它有这些可选的值&#xff1a; 哦。 …

i春秋-登陆(sql盲注爆字段,.git缓存利用)

练习平台地址 竞赛中心 题目描述 先登陆再说 题目内容 就是一个登录框 测试登录 用户名&#xff1a;admin or 11# 密码&#xff1a;随便输 返回密码错误 用户名&#xff1a;随便输 密码&#xff1a;随便输 返回用户名不存在 这里就可以确定时一个bool盲注了 这里提供一个lik…

探索KubeVirt:如何利用InfiniBand提升虚拟机性能

在高性能计算&#xff08;HPC&#xff09;中&#xff0c;网络性能对于集群效率起着至关重要的作用。为了支持大规模并行计算&#xff0c;HPC集群通常依赖高带宽、低延迟的网络&#xff0c;而InfiniBand&#xff08;IB&#xff09;正是其中的首选技术。它能够提供超过100Gbps的带…

基于树莓派的边缘端 AI 目标检测、目标跟踪、姿态估计 视频分析推理 加速方案:Hailo with ultralytics YOLOv8 YOLOv11

文件大纲 加速原理硬件安装软件安装基本设置系统升级docker 方案Demo 测试目标检测姿态估计视频分析参考文献前序树莓派文章hailo加速原理 Hailo 发布的 Raspberry Pi AI kit 加速原理,有几篇文章介绍的不错 https://ubuntu.com/blog/hackers-guide-to-the-raspberry-pi-ai-ki…

小白进!QMK 键盘新手入门指南

经常玩键盘的伙伴应该都知道&#xff0c;现在的键盘市场可谓是百花齐放&#xff0c;已经不是之前的单一功能产品化时代。我们可以看到很多诸如&#xff1a;机械轴键盘、磁轴键盘、光轴键盘、电感轴键盘&#xff0c;以及可能会上市的光磁轴键盘&#xff0c;更有支持屏幕的、带旋…

《操作系统 - 清华大学》3 -3:连续内存分配:内存碎片与分区的动态分配

文章目录 0. 概述1. 内存碎片问题2. 动态分配3. 首次适配算法4. 最优适配算法5. 最差适配算法 0. 概述 内存分配是操作系统管理过程中很重要的环节&#xff0c;首先需要考虑的是一块连续区域分配的过程&#xff0c;这个过程中会有很多问题&#xff0c;首先比较关注的一个问题是…

vue内置指令和自定义指令

常见的指令&#xff1a; v-bind : 单向绑定解析表达式, 可简写为 :xxx v-model : 双向数据绑定 v-for : 遍历数组/对象/字符串 v-on : 绑定事件监听, 可简…

蓝桥杯备赛(持续更新)

16届蓝桥杯算法类知识图谱.pdf 1. 格式打印 %03d&#xff1a;如果是两位数&#xff0c;将会在前面添上一位0 %.2f&#xff1a;会保留两位小数 如果是long&#xff0c;必须在数字后面加上L。 2. 进制转化 2.1. 十进制转任意进制&#xff1a; 十进制转任意进制时&#xff…

vue 项目使用 nginx 部署

前言 记录下使用element-admin-template 改造项目踩过的坑及打包部署过程 一、根据权限增加动态路由不生效 原因是Sidebar中路由取的 this.$router.options.routes,需要在计算路由 permission.js 增加如下代码 // generate accessible routes map based on roles const acce…

TensorFlow 2.0 环境配置

官方文档&#xff1a;CUDA Installation Guide for Windows 官方文档有坑&#xff0c;windows的安装指南直接复制了linux的指南内容&#xff1a;忽略这些离谱的信息即可。 可以从官方文档知悉&#xff0c;cuda依赖特定版本的C编译器。但是我懒得为了一个编译器就下载整个visua…

【计算机网络】【传输层】【习题】

计算机网络-传输层-习题 文章目录 10. 图 5-29 给出了 TCP 连接建立的三次握手与连接释放的四次握手过程。根据 TCP 协议的工作原理&#xff0c;请填写图 5-29 中 ①~⑧ 位置的序号值。答案技巧 注&#xff1a;本文基于《计算机网络》&#xff08;第5版&#xff09;吴功宜、吴英…

HarmonyOS本地存储-Preferences(用户首选项)的使用

一&#xff0c;用户首选项简述 ohos.data.preferences (用户首选项) 用户首选项为应用提供Key-Value键值型的数据处理能力&#xff0c;支持应用持久化轻量级数据&#xff0c;并对其修改和查询。 数据存储形式为键值对&#xff0c;键的类型为字符串型&#xff0c;值的存储数据…

基于Java Web 的家乡特色菜推荐系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…