在 Android 中使用 C/C++:初学者综合指南

在 Android 中使用 C/C++:初学者综合指南

  • 一、为什么有人在他们的 Android 项目中需要 C/C++ 支持?
  • 二、了解 C++ 如何集成到 Android 应用程序中
  • 三、C++和Java程序的编译
    • 3.1 Java
    • 3.2 Android ART 和 DEX 字节码
  • 四、使用 JNI 包装 C++ 源代码
  • 五、CMake和Android NDK
    • 5.1 Android NDK 和工具链
    • 5.2 什么是 CMake?
  • 六、共享库和 ABI

Java 作为一种编程语言,具有许多良好的功能,使其成为应用程序开发的首选语言。它独立于平台(因为虚拟机执行)、JIT 编译、多线程支持以及为程序员提供的富有表现力的简单语法。由于其与平台无关的特性,Java 包可以跨 CPU 架构移植,这使得库开发变得更加容易,从而增强了插件、构建工具和实用程序包的整体生态系统。

功能数量与性能之间恰好存在权衡。像汇编这样的语言具有最少的内存和执行开销,但从程序员的角度来看,其功能数量也最少。在层次结构中向上移动,C 和 C++ 等语言提供了一组良好的功能,同时保持更接近底层硬件。在它们之上的是Java和Python等语言,它们选择通过使用虚拟机来完全消除平台依赖。用这些语言编写的程序有巨大的开销,但却是开发人员的天堂。

JVM 现在可以访问共享库中公开的函数,并且操作系统根据需要执行它们。

一、为什么有人在他们的 Android 项目中需要 C/C++ 支持?

正如我们上面的讨论,在我们的系统中,性能比开发人员友好性更重要,这将我们的重点从 Java/Kotlin 转移到了“原生语言”(C/C++)。让我们通过一些示例来了解本机代码的作用及其性能改进,

  • 图形、渲染和交互:在 Jetpack Compose 等高级框架中,开发用户界面并使其看起来有吸引力似乎是小菜一碟。在像素级别,需要进行数千次计算来计算阴影的强度、闪电模式和物体的纹理。这些计算涉及大量使用线性代数结构,例如向量和矩阵及其各自的运算。处理触摸交互,包括处理移动屏幕上触摸传感器的原始坐标,以及区分单击、双击、拖动或滑动手势,也需要大量计算。这些计算可以用更接近硬件的语言更好地执行,从而可以执行额外的优化。
  • 机器学习:C/C++ 的作用很容易理解,因为 PyTorch 和 TensorFlow 等流行框架的代码库的主要部分是用 C/C++ 编写的。TensorFlow 使用用 C++ 编写的操作,并提供包装器(接口)来使用 Python 代码中的这些操作。C++ 的采用是显而易见的,作为线性代数运算的代码库,CUDA(用于并行处理)是多年前编写的,并且已经经过多年的考验。使用Python作为TensorFlow的接口之一,只是为了让C/C++的东西看起来整洁,并且对于非编程用户来说也很容易。

img

图:TensorFlow 源代码片段,显示了 arg_max 操作的实现。图片参考:作者截图自GitHub上TensorFlow官方源代码

许多此类系统在牺牲可读性和其他一些因素的情况下维护性能。接下来,我们将简短讨论指令集架构 (ISA) 以及程序执行如何随着 CPU 架构的变化而变化。

二、了解 C++ 如何集成到 Android 应用程序中

img

图 1:Android 应用程序如何使用 C++ 源代码的描述。

如上图所示,描述了 Android 中 C/C++ 代码的使用,其中存在两个独立的构建过程,一个用于 C/C++ 代码,另一个用于 Java/Kotlin 代码。在本博客中,我们将重点关注 C/C++ 代码构建过程,并了解代码如何与 JVM 通信以进行函数调用。

我们首先简要概述一下 C/C++ 和 Java 程序的编译方式,主要强调 C/C++ 编译的平台特定性。接下来,我们讨论 JNI,它充当 C/C++ 和 Java 代码之间的粘合剂。我们结束对 CMake、共享库和 ABI 的讨论,它们是构建过程的最底层组件。

正如 Reddit 用户建议的那样pjmlp,Android 不使用 JVM 在设备上运行应用程序。它有自己的运行时,ART(Android RunTime)继承了 Dalvik 及其自定义字节码 DEX。

让我们开始吧🚀

三、C++和Java程序的编译

➡️ C++ 是一种编译语言,源代码被转换为可执行的二进制代码。可执行文件包含源程序的二进制版本、常量和库代码(如果需要)。

➡️ 该可执行文件由操作系统的一个组件(称为加载程序)解析,该组件为程序的执行分配内存并从可执行文件中读取指令。例如,如果一个hello-world C++ 程序是g++在 Ubuntu 上编译的,那么它也可以在其他一些 Linux 发行版上运行,只要它们理解x86x86_64指令集。

arm➡️ 移动设备在指令集上运行arm64,因此编译的程序x86将无法工作,因为两个可执行文件都是用完全不同的语言编写的(如加载程序所见)。

img

图 2:C++ 和 Java 程序的编译。Java 编译器生成的类文件与平台无关,而 C++ 编译器(如 GCC)生成的可执行文件(或共享库)与平台相关。Java类文件需要目标机器上的JVM来执行程序,而C++可执行文件可以直接使用操作系统提供的链接器和加载器运行。

img

一个 Android 项目,包含四种不同平台/架构的库。(作者截图)

Android 设备主要可以在四种架构上运行- arm64-v8aarmeabi-v7ax86x86_64arm-架构还适用于大多数 Android 手机中使用的基于 ARM 的处理器,而x86-基于 ARM 的处理器则用于 Intel 或 AMD 处理器,例如 Windows 模拟器和 Chromebook。

3.1 Java

➡️ 如果您在某个时间点学习过 Java,视频和博客中经常强调的一个显着功能是平台独立性构建一次,随处运行。Java 不是将源代码转换为机器相关的可执行格式,而是将代码转换为中间表示 (IR)。

x86➡️ IR 与平台无关,这意味着无论指令集有何arm差异,平台上生成的 IR都是相同的。IR 由一个依赖于平台的组件(称为 Java 虚拟机)进行解析,该组件从中读取指令并在底层 CPU 上执行它们。由于 JVM 一只手负责 IR,另一只手负责机器的 CPU,因此它与平台无关。

JVM 支持即时 (JIT) 编译,与纯解释性语言相比,这种技术可以提供巨大的性能提升。

➡️ JVM 可以在几乎所有 CPU 架构上运行,并执行在任何平台上编写的 Java 代码(因为生成的 IR 与平台无关),唯一的依赖项是我们需要在目标计算机上安装 JVM。

总而言之,Java 和 C++ 有不同的编译策略,关键是 C++ 执行依赖于体系结构,因此如果我们尝试将 C++ 与任何体系结构中立的语言(如 Java)一起使用,我们需要确保 C++ 依赖关系尊重不同的编译策略。他们将在其上运行的架构。

3.2 Android ART 和 DEX 字节码

Android 作为一个操作系统,并不使用标准的 JVM 来执行 Java 代码。打包的应用程序 APK 包含 DEX 文件(类似于.class文件)以及本机代码和资源。DEX 文件由操作系统提前(在执行之前)编译为本机可执行代码,当用户打开应用程序时,可以快速实例化这些代码。

四、使用 JNI 包装 C++ 源代码

JNI 或 Java 本机接口是一个允许 JVM 和本机代码(C、C++ 或汇编代码)之间轻松通信的框架。一般来说,它提供外部函数接口(FFI),允许用一种语言编写的代码与用另一种语言编写的代码进行通信,通常是通过函数调用的方式。Java 源代码可以搜索 C++ 模块中存在的函数定义,这些函数被标记为供 JVM 使用。

// C++ source file
extern "C" JNIEXPORT jstring JNICALL
Java_com_projects_ml_samplecppdemo_MainActivity_compute(
        JNIEnv* env,
        jobject instance ,
        jstring message , 
        jlong length
) {
    // Method block goes here
}

compute将在 中具有等效的 Kotlin 函数MainActivity

// Kotlin source file
external fun compute( message: String , length: Long ): String

在编译时,JVM 需要找到我们在代码中声明的MainActivity.kt函数的定义。compute我们知道,定义包含在C++源文件中,那么我们如何将其提供给Java程序呢?我们编译 C++ 代码并将其打包为共享库,JVM 将在其中搜索 JNI 函数的定义。

五、CMake和Android NDK

CMake和Android NDK在C/C++源代码编译中的作用
img

5.1 Android NDK 和工具链

我们在基于 Windows、macOS 或 Linux 的操作系统上开发 Android 应用程序。这些系统大多数没有 Android 特定的 ARM 架构,并且无法在 Android 设备上编译代码。那么我们如何为手机使用的Android特定ARM架构编译代码呢?

img

Android NDK 中存在的工具链概述。

我们使用 Android NDK(Android 本机开发套件),它提供编译器和链接器来从x86甚至其他arm设备(Apple Silicon 或 Raspberry Pi)构建 Android-ARM 库和可执行文件。在运行其他目标(例如 Android-ARM)的系统上为其他目标(例如 Android-ARM x86_64)构建代码的过程称为交叉编译。因此,在 Windows 计算机上,使用 Android NDK 的编译器,我们可以为应用程序构建共享库,该应用程序可以在移动设备(即 ARM 设备)上完美运行。

Android NDK 中有一个CMAKE_TOOLCHAIN_FILE,它通知 CMake 使用哪个编译器。正如维基百科所说,工具链是一组编程工具,用于执行复杂的软件开发任务或创建软件产品,Android NDK为不同的Android API级别提供了各种工具链来构建和编译C/C++程序。

5.2 什么是 CMake?

g++如果我们要编译一个简单的 C++ hello-world 程序,我们将使用大多数 Linux 发行版中预安装的GNU编译器,

g++ main.cpp -o main

➡️ 对于单个源文件,main.cpp单个命令即可完成工作。较大的代码库可能具有多个模块和大量 C/C++ 源文件,这些文件必须编译或构建到共享/静态库中。此类代码库(其他 C++ 项目)的依赖关系需要很好地集成。如此庞大的代码库也需要大量的编译时间。

➡️为了解决这些问题,Make可以使用GNU的工具,它提供了管理多个目标、增量构建、包含头文件的能力以及支持多种语言的功能。因此,单个Make脚本将有效地执行编译,而不是运行多个命令进行编译。

cmake_minimum_required(VERSION 3.22.1)

project("samplecppdemo")

# Tell CMake to build a shared library (.so) for the given
# source file native-lib.cpp.
# native-lib.cpp also contains the JNI functions
add_library(
        ${CMAKE_PROJECT_NAME} 
        SHARED
        native-lib.cpp)

# CMake can also link other libraries to the current build
# android and log are used to provide android-specific routines
# and logging respectively
target_link_libraries(
        ${CMAKE_PROJECT_NAME}
        android
        log)

➡️ CMake 可以Make以独立于编译器的方法生成脚本,并具有自己的语法,允许开发人员添加依赖项、标头和其他必须在编译时链接的库。CMake 类似于 Gradle,因为两者都是构建系统。

六、共享库和 ABI

➡️ C/C++ 代码的编译可以生成可执行文件或库,两者都包含源代码的二进制表示形式。main可执行文件具有其他详细信息,例如执行开始处的函数地址并遵循 ELF 格式。库通过将库与程序的目标代码链接来提供可由其他程序调用的函数。

img

Android 应用程序中每个目标架构的 .so 库

➡️ 在 Android 中,C/C++ 文件被编译为共享库,以.so(共享对象)扩展名结尾。这些库公开了我们在 (2) 中编写的 JNI 函数,正如它们extern在原型中所标记的那样。JVM 可以查看.so文件的代码并使用函数的二进制代码在设备上执行它。

➡️ 这种发生在二进制级别的源代码和库代码之间的交互通常是通过应用程序二进制接口 (ABI) 发生的。相反,应用程序编程接口 (API) 在编译发生之前促进源代码级别的此类交互。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【讲座分享】| 复旦大学张奇教授——《自然语言发表论文如何打怪升级?NLP顶会论文发表》

文章目录 1 基础关1.1 基础书籍1.2 提高书籍1.3 课程链接1.4 编程实战 2 阅读关2.1 分层过滤2.2 集团作战,信息获取2.3 论文如何泛读 3 动机 方向关3.1 快速发论文3.2 好的研究 4 写作关4.1 论文写作流程4.2 从读者角度出发4.3 每一部分怎么写4.3.1 Abstract摘要4.3…

浅谈一下软件 QA 方法论 和 工具

浅谈一下软件 QA 方法论 和 工具 目录概述需求: 设计思路实现思路分析1.QA方法论2.Java QA工具 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make a better result…

探索Go 语言URL:解析与构建

探索Go 语言URL:解析与构建 在 Go 语言中,解析和处理 URL 是日常开发中常见的任务之一。URL(统一资源定位符)是指定 Web 资源位置的标准方式,它由多个部分组成,包括协议、主机、路径、查询参数等。本文将深…

调试小结:PHY初始化前后Link Status是否能正确反应网线插上、拔下状态

1 说明 为了验证是否需要初始化PHY才能检测到网线插上、拔下,这里我们对比初始化PHY(LAN8720)前后,插拔网线PHY寄存器1的bit2的是否按照预期变化来进行测试。 我们查看的PHY寄存器是1的bit2,定义如下: 2…

spring框架(一)

1、Spring框架:IoC和AOP 服务端三层开发:表现层、业务层、持久层 ssm, springboot, springcloud(微服务,治理组件) Spring框架是一个流行的Java应用程序框架,它提供了许多功能来简化企业级应用程序的开发。其中,控制反…

[每日一题] 01.30

文章目录 数列求和质数口袋奇怪数求和 数列求和 n int(input()) print(sum([i for i in range(1,n 1)]))质数口袋 n int(input()) i 2 sum 0 count 0 while n - i > sum:flag Truefor j in range(2,i): # 判断i是否为素数if i % j 0:flag Falsebreakif flag:sum i…

【LLM多模态】Cogview3、DALL-E3、CogVLM、CogVideo模型

note 文章目录 noteVisualGLM-6B模型图生文:CogVLM-17B模型1. 模型架构2. 模型效果 文生图:CogView3模型DALL-E3模型CogVideo模型网易伏羲-丹青模型Reference VisualGLM-6B模型 VisualGLM 是一个依赖于具体语言模型的多模态模型,而CogVLM则是…

BL808学习日志-3-DPI-RGB屏幕使用-LVGL D0

一、DPI-RGB驱动 BL808的手册上显示是支持RGB565屏幕显示输出的,但是一直没找到网上的使用例程。且官方的SDK显示也是能够使用的,只是缺少了驱动。这一部分驱动在SIPEED的SDK中已经内置了,今天就是简单的点亮一个800*480 RGB565的屏幕。 二、…

如何优化博客的内容和用户体验

在当今数字时代,博客成为了分享知识、展示个人专业能力和吸引读者的重要工具。然而,随着越来越多的博客涌现,如何优化博客的内容和用户体验成为了一个关键的问题。本文将为你提供一些有效的技巧,帮助你优化博客的内容和提升用户体…

开启公网即访问:GpuMall平台的云服务解决方案

在人工智能的黄金时代,数据科学家和AI工程师正面临着一个共同的挑战:如何快速、有效地将开发中的服务部署至公网,以应对日益增长的计算和访问需求。GpuMall智算云平台提供了一个全面的解决方案,让这一切变得简单。本文将深入探讨G…

【C++】C++入门—— 引用

引用 1 前情提要2 概念剖析3 引用特性4 常引用5 使用场景5.1做参数5.2 做返回值 6 传值 传引用的效率比较7 引用与指针的差异Thanks♪(・ω・)ノ谢谢阅读下一篇文章见 1 前情提要 在C语言中,我们往往会遇见复杂的指针(如…

python 写入csv文件 内容乱码

问题 python 写入csv文件 内容乱码 详细问题 笔者核心代码 import csv # 将数据写入 CSV 文件 csv_file_path "soil_data.csv" header ["经度", "纬度", "土壤类型", "pH值"]with open(csv_file_path, mode"w&q…

java 社区资源管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java Web社区资源管系统是一套完善的java web信息管理系统 ,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql5.…

vue3封装el-pagination分页组件

1、效果如图&#xff1a; 2、分页组件代码&#xff1a; <template><div class"paging"><el-config-provider :locale"zhCn"><el-paginationv-model:current-page"page.currentPage"v-model:page-size"page.pageSize…

如何更新github上fork的项目(需要一定git基础)

如何更新Fork的项目(需要一定git基础) 前言&#xff1a;本文记录一下自己在github上fork了大佬的开源博客项目https://github.com/tangly1024/NotionNext&#xff0c;如何使用git克隆以及自定义开发和同步合并原项目更新迭代内容的的步骤 如何更新fork的项目(进阶版) 首先你…

DML的原理:一篇文章让你豁然开朗

推荐阅读 给软件行业带来了春天——揭秘Spring究竟是何方神圣&#xff08;一&#xff09; 给软件行业带来了春天——揭秘Spring究竟是何方神圣&#xff08;二&#xff09; 文章目录 推荐阅读DML 数据操纵语言INSERT语句UPDATE语句DELETE语句SELECT语句 DML 数据操纵语言 DML是…

深入了解Yum:Linux系统的软件包管理利器

目录 软件包 软件包的来源 关于yum yum是什么 yum的相关操作 介绍rzsz rz&#xff08;从Windows本地传到Linux服务器&#xff09; sz&#xff08;从Linux服务器传到Windows本地&#xff09; 注意事项 查看软件包 安装软件 卸载软件 yum的本地配置 为什么要进行配置…

UDP/TCP协议特点

1.前置知识 定义应用层协议 1.确定客户端和服务端要传递哪些信息 2.约定传输格式 网络上传输的一般是二进制数据/字符串 结构化数据转二进制/字符串 称为序列化 反之称之为反序列化 下面就是传输层了 在TCP/IP协议中,我们以 目的端口,目的IP 源端口 源IP 协议号这样一个五…

蓝桥杯-常用STL(一)

常用STL &#x1f388;1.动态数组&#x1f388;2.vector的基础使用&#x1f52d;2.1引入库&#x1f52d;2.2构造一个动态数组&#x1f52d;2.3插入元素&#x1f52d;2.4获取长度并且访问元素&#x1f52d;2.5修改元素&#x1f52d;2.6删除元素&#x1f52d;2.7清空 &#x1f38…

Redis核心技术与实战【学习笔记】 - 8.Redis 时间序列数据处理

在做 web 产品是&#xff0c;都会有这么一个需求&#xff1a; 记录用户在网站或 APP 上的点击行为数据&#xff0c;来分析用户行为。这里的数据一般包括用户 ID、行为类型&#xff08;如浏览、登录、下单等&#xff09;、行为发生的时间戳。 userID, type, timeStamp 与之类似&…