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

【Android Gradle学习笔记】第八天:NDK的使用

**加粗样式**

NDK

NDK(Native Development Kit)是 Android 平台用于开发原生代码(C/C++)的工具集,它允许开发者在 Android 应用中集成高性能的原生模块(如游戏引擎、音视频处理、加密算法等)。

而 Gradle 作为 Android 官方构建系统,通过 Android Gradle Plugin(AGP) 提供了对 NDK 的深度支持,简化了原生代码的编译、打包和集成流程。

本文将从基础概念出发,结合一个完整的「Hello World」示例,详细讲解 Android Gradle 如何支持 NDK 开发,包括环境配置、Gradle 脚本解析、原生代码编写与调用等核心环节。

NDK 与 Gradle 的协作关系

在 Android 项目中,Java/Kotlin 代码运行在 Dalvik/ART 虚拟机上,而原生代码(C/C++)通过编译生成 .so 动态链接库,最终被打包到 APK 中。

Gradle 的作用是:

  1. 自动调用 NDK 工具链(如编译器 clang、链接器 ld)编译 C/C++ 代码;
  2. 将生成的 .so 库与 Java/Kotlin 代码整合,生成最终的 APK;
  3. 支持配置编译参数(如目标 CPU 架构、C++ 标准版本等)。

AGP 对 NDK 的支持主要依赖两种构建工具:

  • CMake:跨平台构建工具(推荐,AGP 默认集成);
  • ndk-build:NDK 传统构建工具(基于 Makefile,适用于旧项目)。

本文以 CMake 为例,讲解现代 Android 原生开发流程。

安装 NDK 和 CMake

在使用 Gradle 构建原生代码前,需确保开发环境中已安装 NDK 和 CMake:

  1. 打开 Android Studio,进入 File > Settings > Appearance & Behavior > System Settings > Android SDK
  2. 切换到 SDK Tools 标签,勾选:
    • NDK (Side by side):选择一个稳定版本(如 25.2.9519653);
    • CMake:选择与 NDK 兼容的版本(通常默认即可);
  3. 点击「Apply」安装,Android Studio 会自动配置环境变量。

创建支持 NDK 的项目

方式 1:新建项目时直接包含 C++ 支持

  1. 打开 Android Studio,点击「New Project」;
  2. 选择「Native C++」模板(位于「Phone and Tablet」分类下);
  3. 填写项目名称(如「NdkHelloWorld」),选择语言(Java 或 Kotlin),点击「Next」;
  4. 在「Customize C++ Support」页面,可配置:
    • C++ Standard:选择 C++ 标准(如 C++17);
    • Exceptions Support:是否启用 C++ 异常;
    • Runtime Type Information Support:是否启用 RTTI;
  5. 点击「Finish」,Android Studio 会自动生成包含原生代码的项目结构。

方式 2:在现有项目中添加 NDK 支持

  1. 在现有项目的 app/src/main 目录下,创建 cpp 文件夹(用于存放 C/C++ 代码);
  2. cpp 目录下创建 CMakeLists.txt(CMake 构建脚本);
  3. 配置模块级 build.gradle(见下文「Gradle 配置详解」)。

四、项目结构解析:原生代码与配置文件

以「方式 1」创建的项目为例,核心结构如下:

app/
├── src/
│   ├── main/
│   │   ├── java/com/example/ndkhelloworld/  # Java/Kotlin 代码
│   │   │   └── MainActivity.kt              # 调用原生方法的入口
│   │   ├── cpp/                             # C/C++ 代码
│   │   │   └── native-lib.cpp               # 原生实现代码
│   │   └── CMakeLists.txt                   # CMake 构建脚本
│   └── ...
├── build.gradle                              # 模块级 Gradle 配置
└── ...

关键文件说明:

  • native-lib.cpp:存放 C/C++ 实现代码(如 Hello World 逻辑);
  • CMakeLists.txt:告诉 CMake 如何编译原生代码(指定源文件、输出库名等);
  • build.gradle:配置 Gradle 与 NDK 的协作规则(如目标架构、编译参数等)。

连接 Java 与原生代码

模块级 build.gradle(通常是 app/build.gradle)是 Gradle 支持 NDK 的核心配置文件,以下是关键配置项解析:

完整配置示例(AGP 7.0+)

android {compileSdk 33defaultConfig {applicationId "com.example.ndkhelloworld"minSdk 21targetSdk 33versionCode 1versionName "1.0"// 配置原生代码编译参数externalNativeBuild {cmake {// C++ 编译选项(如启用 C++17 标准)cppFlags "-std=c++17"// 指定生成的 .so 库支持的 CPU 架构(默认生成所有架构)abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'}}}// 配置 CMake 构建脚本路径externalNativeBuild {cmake {path "src/main/CMakeLists.txt"  // 指向项目中的 CMakeLists.txtversion "3.22.1"                // 指定 CMake 版本(可选)}}// 可选:配置 NDK 版本(若未指定,使用 SDK 中安装的默认版本)ndkVersion "25.2.9519653"
}dependencies {implementation 'androidx.core:core-ktx:1.7.0'implementation 'androidx.appcompat:appcompat:1.6.1'// ... 其他依赖
}

核心配置项说明

  1. android.defaultConfig.externalNativeBuild
    配置原生代码的编译参数,如 cppFlags(C++ 编译选项)、abiFilters(目标 CPU 架构)。

    • abiFilters 用于限制生成的 .so 库架构(减少 APK 体积),常见架构:
      • arm64-v8a:64 位 ARM 设备(主流手机);
      • armeabi-v7a:32 位 ARM 设备(旧设备);
      • x86/x86_64:模拟器或 x86 架构设备。
  2. android.externalNativeBuild
    指定 CMake 构建脚本的路径(path)和版本(version),Gradle 会通过该脚本找到原生代码并编译。

  3. android.ndkVersion
    显式指定 NDK 版本(推荐),避免因默认版本变更导致编译错误。

CMake 构建脚本

CMakeLists.txt 是 CMake 的构建脚本,用于定义如何编译 C/C++ 代码。以下是「Hello World」示例的 CMakeLists.txt

完整示例

# 指定 CMake 最低版本(与 AGP 兼容即可)
cmake_minimum_required(VERSION 3.18.1)# 声明项目名称(可选,仅用于标识)
project("ndkhelloworld")# 编译原生库:
# 第一个参数:库名称(如 "native-lib",最终生成 libnative-lib.so)
# 第二个参数:库类型(SHARED 表示动态库,STATIC 表示静态库)
# 后续参数:源文件路径(可多个)
add_library(native-libSHAREDsrc/main/cpp/native-lib.cpp
)# 查找 Android 系统库(如 log 库,用于打印日志)
find_library(log-liblog  # 对应 Android 的 liblog.so
)# 链接库:将 native-lib 与 log-lib 链接(如需使用日志功能)
target_link_libraries(native-lib${log-lib}
)

核心命令说明

  • add_library:定义要编译的原生库,指定库名、类型(动态库 SHARED 是 Android 常用类型)和源文件。
  • find_library:查找系统预编译的库(如 log 库用于 __android_log_print 打印日志)。
  • target_link_libraries:将自定义库与系统库链接,确保代码中能调用系统库的函数。

从 C++ 实现到 Java/Kotlin 调用

步骤 1:实现 C++ 原生方法

native-lib.cpp 中编写返回「Hello from C++」字符串的函数,需遵循 JNI(Java Native Interface)命名规范:

#include <jni.h>
#include <string>// JNI 函数命名规则:Java_包名_类名_方法名(包名中的 "." 替换为 "_")
// 这里对应 Java/Kotlin 中的 "stringFromJNI()" 方法
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ndkhelloworld_MainActivity_stringFromJNI(JNIEnv* env,        // JNI 环境指针(用于调用 JNI 函数)jobject /* this */  // 调用该方法的 Java 对象(MainActivity 实例)
) {std::string hello = "Hello from C++";// 将 C++ 字符串转换为 Java String 并返回return env->NewStringUTF(hello.c_str());
}
  • extern "C":确保 C++ 编译器不修改函数名(避免 JNI 调用时找不到函数);
  • JNIEXPORTJNICALL:JNI 关键字,指定函数可被 JVM 调用;
  • jstring:JNI 类型(对应 Java 的 String);
  • JNIEnv*:提供 JNI 函数接口(如 NewStringUTF 用于创建 Java 字符串)。

步骤 2:在 Java/Kotlin 中声明并调用原生方法

以 Kotlin 为例,在 MainActivity.kt 中声明原生方法并加载 .so 库:

package com.example.ndkhelloworldimport androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextViewclass MainActivity : AppCompatActivity() {// 声明原生方法(需与 C++ 函数名对应)external fun stringFromJNI(): Stringoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 调用原生方法并显示结果val tv: TextView = findViewById(R.id.sample_text)tv.text = stringFromJNI()}// 静态代码块:加载编译生成的原生库(库名与 CMakeLists 中定义的一致)companion object {init {System.loadLibrary("native-lib")}}
}
  • external:Kotlin 关键字,标识该方法由原生代码实现;
  • System.loadLibrary("native-lib"):加载 libnative-lib.so 库(库名省略前缀 lib)。

构建

构建项目

点击 Android Studio 工具栏的「Sync Project with Gradle Files」同步配置,然后点击「Make Project」(或 Ctrl+F9)编译项目。

Gradle 会执行以下操作:

  1. 调用 CMake 根据 CMakeLists.txt 编译 native-lib.cpp,生成对应架构的 .so 库;
  2. .so 库复制到 app/build/intermediates/cmake/debug/obj/ 目录;
  3. 打包 .so 库和 Java/Kotlin 代码到 APK 中。

运行应用

连接 Android 设备或启动模拟器,点击「Run」按钮运行应用。若配置正确,屏幕会显示:

Hello from C++

查看生成的 .so 库

编译成功后,可在以下路径找到各架构的 .so 库:

app/build/intermediates/cmake/debug/obj/<架构>/libnative-lib.so

总结

  1. 配置解析:Gradle 读取 build.gradle 中的 externalNativeBuild 配置,定位 CMakeLists.txt
  2. 原生编译:调用 CMake 和 NDK 工具链(clang)编译 C/C++ 代码,生成 .so 库;
  3. 依赖整合:将 .so 库与 Java/Kotlin 字节码一起打包到 APK 的 lib/<架构>/ 目录;
  4. 运行时加载:应用启动时,System.loadLibrary 加载 .so 库,JVM 通过 JNI 调用原生方法。

常见问题与解决方案

  1. 「找不到原生方法」错误
    检查 JNI 函数名是否与 Java/Kotlin 方法名、包名一致(注意下划线和大小写)。

  2. 编译失败:未找到 NDK
    build.gradle 中显式指定 ndkVersion,或在 SDK Tools 中重新安装 NDK。

  3. APK 体积过大
    通过 abiFilters 只保留目标架构(如仅保留 arm64-v8a),减少冗余 .so 库。

  4. C++ 标准不兼容
    cppFlags 中指定正确的标准(如 -std=c++17),并确保 NDK 版本支持该标准。
    > (注:文档部分内容可能由 AI 生成)

http://www.dtcms.com/a/508556.html

相关文章:

  • OpenCV(十三):通道的分离与合并
  • 【猿辅导-注册安全分析报告-无验证方式导致安全隐患】
  • 基于YOLOv8与SCConv的轻量化目标检测模型-协同优化空间与通道特征重构
  • 卫计网站建设工作计划怎么做网页商城
  • 攻略做的比较好的网站邯郸电商设计
  • 青海某公路水渠自动化监测服务项目
  • 大厂级企业后端:配置变更与缓存失效的自动化处理方案
  • 打破协议壁垒:耐达讯自动化Modbus转Profinet网关实现光伏逆变器全数据采集
  • 深圳创新网站建设适合穷人的18个创业项目投资小
  • Docker部署RocketMQ时Broker IP地址问题及解决方案
  • 生产环境定时器陷阱:CLOCK_REALTIME与CLOCK_MONOTONIC的生死抉择
  • 建设电子商务网站流程网站改版汇报
  • 长清网站建设费用长椿街网站建设
  • 算法学习——技巧小结7(回溯:排列、组合、子集)
  • Qt Designer 中实现布局比例的方法和简单实例
  • 黄埔网站建设(信科网络)找工程项目上哪个平台好呢
  • 想要提高网站排名应该怎么做直播系统开发
  • Doris专题22- 数据更新-概述
  • 立创EDA专业版使用技巧——按TAB暂停修改
  • 伊犁网站建设公司网页设计与制作实训步骤
  • 深圳网站建设外贸公司软件公司简介内容怎么写
  • 亚太稀土产链
  • 【源码+文档+调试讲解】基于SpringBoot + Vue的知识产权管理系统 041
  • 建设银行信用卡管理中心网站首页网站不换域名换空间
  • 小型企业做网站的价格图片主题wordpress
  • 【C/C++】进程
  • 如何让我们的网站新闻被百度新闻收录网站现在用h5做的吗
  • composer安装 laravel 指定版本
  • 手机购物网站建设网站后期维护包括
  • 服装 多语言 网站源码兰州网站建设和推广