4.2 Android NDK 基础概念

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.xdnf.cn/news/16813.html

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

相关文章

【Visual Studio】设置文件目录

打开属性 输出目录&#xff1a;$(SolutionDir)bin\$(Platform)\$(Cinfiguration)\ 中间目录&#xff1a;$(SolutionDir)bin\intermediates\$(Platform)\$(Cinfiguration)\

基于Java的校园菜鸟驿站管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

Photoshop(PS)——人像磨皮

1.新建一个文件&#xff0c;背景为白色&#xff0c;将图片素材放入文件中 2.利用CtrlJ 复制两个图层出来&#xff0c;选择第一个拷贝图层&#xff0c;选择滤镜---杂色---蒙尘与划痕 3.调整一下数值&#xff0c;大概能够模糊痘印痘坑&#xff0c;点击确定。 4.然后选择拷贝2图层…

Yocto - 使用Yocto开发嵌入式Linux系统_13 创建定制层

Creating Custom Layers 除了使用社区或供应商提供的现有图层外&#xff0c;我们还将在本章中学习如何为我们的产品创建图层。此外&#xff0c;我们还将了解如何创建机器定义和分布&#xff0c;并从中获益&#xff0c;从而更好地组织我们的源代码。 In addition to using exist…

第5章-总体设计 5.2 需求转化为规格

5.2 需求转化为规格 1.框式产品&#xff08;1&#xff09;业务规格&#xff0c;这需要满足客户期望、有市场竞争力、颗粒度最合理。&#xff08;2&#xff09;整框规格&#xff0c;包括电源、功耗、散热、可靠性的规格&#xff0c;要保证整款满足环境应用要求。&#xff08;3&a…

kali上安装docker,并且生成centos7容器和创建apache容器后台运行

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…

4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明

4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明 文章目录 4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明前言1. Ribbon 介绍1.1 LB(Load Balance 负载均衡) 2. Ribbon 原理2.2 Ribbon 机制 3. Spring Cloud Ribbon 实现负载均衡算法-应用实例4. 总结&#x…

约克VRF中央空调新天氟地水/天氟热水,做冬日生活的温暖守护者

随着冬季的悄然降临,现代人对居家环境的舒适性要求愈发提升,如何在寒冷的季节里营造一个温暖、静谧且健康的居住空间,成为了时下关注的焦点。面对冬日空气干燥、寒气侵袭的挑战,约克VRF中央空调凭借其氟系统和水系统的跨界融合,为家庭带来了纵享四季的恣意体验,让温暖与舒适触手…

计算机提示mfc140u.dll丢失的五种解决方法,了解mfc140u.dll错误的几种修复方法

当你尝试打开某些程序时&#xff0c;突然出现错误提示&#xff0c;告知你系统缺少 mfc140u.dll 文件&#xff0c;这可能让你感到困惑和无助。mfc140u.dll 是 Microsoft Foundation Class (MFC) 库的一部分&#xff0c;对于运行很多由 Visual Studio 2015 使用 MFC 开发的应用程…

【SSL-RL】自监督强化学习: 好奇心驱动探索 (CDE)算法

&#x1f4e2;本篇文章是博主强化学习&#xff08;RL&#xff09;领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅…

Windows系统 ElasticSearch,Kibana安装

目录 1.wins安装ElasticSearch2.将 elasticsearch 以服务的方式安装3. 在系统环境变量 Path 中添加如下路径4.启动点击即可5.双击 elasticsearch.bat 启动 elasticsearch 服务6.启动后第一次会显示一些配置信息,包括默认的用户密码 先记住 记不住的话可以重置密码7.验证安装结果…

《Probing the 3D Awareness of Visual Foundation Models》论文解析——单图像表面重建

一、论文简介 论文讨论了大规模预训练产生的视觉基础模型在处理任意图像时的强大能力&#xff0c;这些模型不仅能够完成训练任务&#xff0c;其中间表示还对其他视觉任务&#xff08;如检测和分割&#xff09;有用。研究者们提出了一个问题&#xff1a;这些模型是否能够表示物体…

P3-4.【结构化程序设计】第四节——知识要点:break、continue和goto辅助循环设计语句

知识要点&#xff1a;break、continue和goto辅助循环设计语句 视频&#xff1a; P3-4.1.【结构化程序设计】第四节——知识要点&#xff1a;break、continue和goto辅助循环设计语句 P3-4.2.【结构化程序设计】第四节——知识要点&#xff1a;break、continue和goto辅助循环设计…

灵神DAY3 KMP算法

具体解释&#xff1a; 1. 真前缀和真后缀的定义 前缀&#xff1a;字符串的起始部分。例如&#xff0c;字符串 s "aabcaa" 的前缀是 ""、"a"、"aa"、"aab"、"aabc"、"aabca"、"aabcaa"。 …

MySQL5.7.37安装配置

1.下载MySQL软件包并解压 2.配置环境变量 3.新建my.ini文件并输入信息 [mysqld] #端口号 port 3306 #mysql-5.7.27-winx64的路径 basedirC:\mysql-5.7.37\mysql-5.7.37-winx64 #mysql-5.7.27-winx64的路径\data datadirC:\mysql-5.7.37\mysql-5.7.37-winx64\data #最大连接数…

基于单片机的手持金属探测仪设计

本设计以STM32F103C8T6单片机为核心&#xff0c;通过金属线圈感应器来判断是否存在金属&#xff0c;控制OLED显示屏显示金属探测仪的灵敏度和参考值&#xff0c;通过电源模块将220V转化为3.3V对单片机进行供电&#xff0c;还可以通过按键对金属探测仪的灵敏度进行设置&#xff…

P1197 星球大战(并查集+逆向思维)

这是今天写的比较有价值的一道题&#xff0c;晚上写了大概一个多小时&#xff0c;主要还是在debug&#xff0c;出得很妙&#xff0c;好题&#x1f44d; P1197 [JSOI2008] 星球大战 - 洛谷 | 计算机科学教育新生态 思路&#xff1a;如果我们按照顺序一个一个的去计算毁灭一个星…

深度学习驱动的蛋白质设计技术与前沿实践-从基础到尖端应用

RoseTTAFold&#xff0c;作为David Baker教授团队早期开发的蛋白质结构预测工具&#xff0c;在学术界与工业界广受认可。然而&#xff0c;随着时间推移&#xff0c;仅局限于预测已知结构的蛋白质并不能满足生物医药和生物工程领域对创新设计的需求。这促使David Baker教授团队继…

Linux 进程信号初识

目录 0.前言 1.什么是信号 1.1生活中的信号 1.2 OS中的信号 2.认识信号 2.1信号概念 2.2查看信号 2.3 signal函数 2.4代码示例 3. 信号处理方式 3.1 忽略信号 3.2 默认处理 3.3 自定义处理 4.小结 &#xff08;图像由AI生成&#xff09; 0.前言 在之前的学习中&#xff0c;我…

SpringBoot(二十五)SpringBoot集成JRebel实现热更新

今天来安装一个IDEA代码热更新的插件,一个神器。 我们之前也为IDEA配置了热更新,使用的是spring-boot-devtools插件。具体请移步《SpringBoot(一)创建项目及配置IDEA热更新》 上边这个热更新对于单模块项目是没有问题的,但是对于多模块项目可能就无能无能为力了,而且,随…