当前位置: 首页 > news >正文

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全局引用,你就可以从任何连接的线程中使用它。

相关文章:

4.2 Android NDK 基础概念

1 JavaVM和JNIEnv JNI 定义了两个关键数据结构&#xff0c;JavaVM和JNIEnv。这两者本质上都是指向函数表指针的指针。&#xff08;在 C 版本中&#xff0c;它们是具有指向函数表的指针的类&#xff0c;以及指向该表的每个 JNI 函数的成员函数。&#xff09;JavaVM提供了“调用接…...

PIL包在Python图像处理中的应用

诸神缄默不语-个人CSDN博文目录 PIL&#xff08;Python Imaging Library&#xff09;是Python中一个强大的图像处理库&#xff0c;尽管其已不再更新&#xff0c;但其后续版本Pillow提供了更多的功能和更好的兼容性。本文将重点介绍Pillow库中的open()函数、fromarray()函数以及…...

ArcGIS Pro ADCore DAML

ArcGIS Pro ADCore DAML ArcGIS Pro SDK - ADCore.daml https://download.csdn.net/download/szy13323042191/89997391...

Clip结合Faiss+Flask简易版文搜图服务

一、实现 使用目录结构&#xff1a; templates ---upload.html faiss_app.py 前端代码&#xff1a;upload.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content&quo…...

【机器学习】数学知识:欧式距离(Euclidean Distance)和曼哈顿距离(Manhattan Distance)

欧式距离和曼哈顿距离是两种常用的距离度量方法&#xff0c;用于衡量两点之间的相似性或差异性。它们在几何分析、数据挖掘、机器学习等领域有广泛应用。 1. 欧式距离 概念 欧式距离&#xff08;Euclidean Distance&#xff09;是最常见的直线距离度量方法&#xff0c;源于欧…...

Redis 概 述 和 安 装

安 装 r e d i s: 1. 下 载 r e dis h t t p s : / / d o w n l o a d . r e d i s . i o / r e l e a s e s / 2. 将 redis 安装包拷贝到 /opt/ 目录 3. 解压 tar -zvxf redis-6.2.1.tar.gz 4. 安装gcc yum install gcc 5. 进入目录 cd redis-6.2.1 6. 编译 make …...

数据仓库面试题集离线实时

一、Flink面试问题集 1、flinkkafka 如何保证精准一次 配置两阶段提交 2、Flink提交方式&#xff0c; 使用pre-job还是yarn-session模式&#xff0c;以及Application模式&#xff0c;好处&#xff1f; Flink提交模式模式对比 3、Flink UV统计实现 set布隆过滤器redis 有误…...

Spring Boot框架:电商系统的技术革新

4 系统设计 网上商城系统的设计方案比如功能框架的设计&#xff0c;比如数据库的设计的好坏也就决定了该系统在开发层面是否高效&#xff0c;以及在系统维护层面是否容易维护和升级&#xff0c;因为在系统实现阶段是需要考虑用户的所有需求&#xff0c;要是在设计阶段没有经过全…...

一键抠图:免费安全的在线图片去除背景工具

利用前端技术&#xff0c;轻松去除图片背景 得益于Webassembly技术的快速发展&#xff0c;前端可以实现的功能越来越多。本文将介绍一款基于briaai的 RMBG-1.4型号的 预训练模型实现的在线图片去除背景(抠图)工具。地址&#xff1a;https://www.potatotools.top/toolsEntrance…...

vue项目PC端和移动端实现在线预览pptx文件

通过PPTXjs插件,实现PPTX文件在线预览,需下载PPTXjs,将其引入HTML页面,并编写相应的HTML和JS代码,如果是移动端还需调整div大小,这是一种便捷的前端PPTX转HTML技术,适合网页展示使用 PPTX在线预览&#xff0c;使用jquery的插件《PPTXjs》&#xff0c;纯前端实现pptx转html进行…...

uniapp适配暗黑模式配置plus.nativeUI.setUIStyle适配DarkMode配置

uniapp适配暗黑模式配置 目录 uniapp适配暗黑模式配置setUIStyleDarkMode 适配app-plus manifest.json配置theme.json配置pages.json配置页面切换代码实现同步手机暗黑配置额外适配 参考官方文档&#xff1a;https://uniapp.dcloud.net.cn/tutorial/darkmode.html 主要用到api…...

EXCEL 或 WPS 列下划线转驼峰

使用场景&#xff1a; 需要将下划线转驼峰&#xff0c;直接在excel或wps中第一行使用公式&#xff0c;然后快速刷整个列格式即可。全列工下划线转为格式&#xff0c;使用效果如下&#xff1a; 操作步骤&#xff1a; 第一步&#xff1a;在需要显示驼峰的一列&#xff0c;复制以…...

走进Linux的历史发展史

目录 前言 Linux的发展史 UNIX发展的历史 Linux发展历史 开源 企业应用现状 Linux在服务器领域的发展 桌面领域 移动嵌入式领域 云计算/大数据领域 发行版 ​编辑 Linux环境搭建方式 前言 本节博客内容较水&#xff0c;主要介绍Linux的发展历史和其相关的学习内容&a…...

学习yum工具,进行安装软件

目录 1.Linux 软件包管理器 yum 什么是软件包 2.Linux下安装软件的方案 3.Linux软件生态 Linux下载软件的过程&#xff08;Ubuntu、Centos、other&#xff09; 操作系统的好坏评估--- ⽣态问题 为什么会有⼈免费特定社区提供软件&#xff0c;还发布&#xff1f;还提供云服…...

union介绍及使用

union格式 在C中&#xff0c;union是一种特殊的数据类型&#xff0c;它允许在相同的内存位置存储不同的数据类型&#xff0c;但在任意时刻只能使用一个成员。以下是union类型的基本格式说明&#xff1a; union UnionName {memberType1 memberName1;memberType2 memberName2;m…...

安全,服务器证书和SSL连接

业务报错&#xff1a; javax.net.ssl.SSLPeerUnverifiedException: Certificate for <10.5.20.137> doesn’t match any of the subject alternative names: [*.dt.zte.com.cn] at org.apache.http.conn.ssl.SSLConnectionSocketFactory.verifyHostname(SSLConnectionSoc…...

Java结合ElasticSearch根据查询关键字,高亮显示全文数据。

由于es高亮显示机制的问题。当全文内容过多&#xff0c;且搜索中标又少时&#xff0c;就会出现高亮结果无法覆盖全文。因此需要根据需求手动替换。 1.根据es的ik分词器获取搜索词的分词结果。 es部分&#xff1a; //中文分词解析 post /_analyze {"analyzer":"…...

Design Compiler:Topographical Workshop Lab2

相关阅读 Design Compilerhttps://blog.csdn.net/weixin_45791458/category_12738116.html?spm1001.2014.3001.5482 本文是对Synopsys Design Compiler Topographical/Graphical Workshop Lab Guide中Lab2的翻译&#xff0c;Lab文件可以从以下链接获取。 Synopsys Design Co…...

【C语言】连接陷阱探秘(1):声明与定义

目录 一、声明与定义的混淆 1.1. 声明(Declaration) 1.2. 定义(Definition) 1.3. 避免混淆的方法 1.4. 示例 二、声明与定义不匹配 2.1. 常见的不匹配情况 2.2. 解决方法 三、外部变量与静态变量的命名冲突 3.1. 外部变量命名冲突 3.2. 静态变量命名冲突 四、缺…...

ChatGPT学术专用版,一键润色纠错+中英互译+批量翻译PDF

ChatGPT academic项目是由中科院团队基于ChatGPT专属定制。论文润色、语法检查、中英互译、代码解释等可一键搞定&#xff0c;堪称科研神器。 功能介绍 我们以3.5版本为例&#xff0c;ChatGPT学术版总共分为五个区域&#xff1a;输入控制区、输出对话区、基础功能区、函数插件…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

安卓基础(Java 和 Gradle 版本)

1. 设置项目的 JDK 版本 方法1&#xff1a;通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分&#xff0c;设置 Gradle JDK 方法2&#xff1a;通过 Settings File → Settings... (或 CtrlAltS)…...

数据库——redis

一、Redis 介绍 1. 概述 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的、高性能的内存键值数据库系统&#xff0c;具有以下核心特点&#xff1a; 内存存储架构&#xff1a;数据主要存储在内存中&#xff0c;提供微秒级的读写响应 多数据结构支持&…...