Android 应用开发的主流语言是 Java 和 Kotlin,它们运行在 Dalvik/ART 虚拟机上,而 C 语言(以及 C++)不能直接用来开发一个完整的、可运行的 Android App UI 界面。

我们为什么要在 Android 开发中用 C 语言呢?答案是:性能、复用和访问底层。
为什么要在 Android 中使用 C/C++?
-
性能优化
- 计算密集型任务:对于游戏物理引擎、音视频编解码、图像/信号处理、科学计算等,C/C++的执行效率远高于 Java/Kotlin,它们可以直接操作内存,没有虚拟机的额外开销。
- 减少 GC 压力:Java/Kotlin 的自动垃圾回收机制在处理大量对象时可能会造成卡顿,C/C++ 手动管理内存,可以精确控制,避免 GC 停顿带来的性能问题。
-
代码复用
- 很多成熟的、高性能的库都是用 C/C++ 编写的,
- FFmpeg:音视频处理库。
- OpenCV:计算机视觉库。
- OpenGL ES:3D 图形渲染 API(虽然 API 是 C 风格,但驱动是 C++ 的)。
- TensorFlow Lite:移动端机器学习推理引擎。
- 通过在 Android 中集成这些 C/C++ 库,可以避免重复造轮子,快速实现高级功能。
- 很多成熟的、高性能的库都是用 C/C++ 编写的,
-
访问硬件和底层系统
(图片来源网络,侵删)- Android 系统本身是基于 Linux 内核的,其很多硬件驱动和系统服务都是用 C/C++ 编写的。
- 当你需要访问特定的硬件功能(如相机传感器、GPS 芯片寄存器)或调用一些不通过 Java API 暴露的底层系统功能时,就需要通过 C/C++ 来实现。
在 Android 中使用 C/C++ 的主要方式
主要有两种方式,它们适用于不同的场景:
- JNI (Java Native Interface):传统的、经典的“胶水”层技术。
- NDK (Native Development Kit):Google 官方提供的工具集,用于简化 JNI 的使用和原生代码的编译。
NDK 是建立在 JNI 之上的一个更高级的工具集。
JNI (Java Native Interface)
JNI 是一个编程框架,它允许运行在虚拟机(JVM)中的 Java 代码与其它语言(如 C/C++)编写的应用程序进行交互。
工作流程:

- Java 端:在 Java 或 Kotlin 代码中,声明一个
native方法。 - 编译生成头文件:使用
javac编译 Java 代码,然后用javah工具(或新版 JDK 的javac -h)生成一个 C/C++ 头文件(.h),这个头文件包含了 Javanative方法的 C 函数声明。 - C/C++ 端:实现头文件中声明的 C/C++ 函数,这个函数的签名必须与 JNI 规范严格匹配,以便虚拟机能找到它。
- 加载库:在 Java 代码中,使用
System.loadLibrary("库名称")来加载编译好的 C/C++ 动态链接库(.so文件)。 - 交互:在 C/C++ 函数中,你可以通过 JNI 提供的 API 来操作 Java 对象、调用 Java 方法,反之亦然。
JNI 的缺点:
- 繁琐:需要手动编写大量胶水代码,数据类型转换(如 Java 的
String和 C 的char*)非常麻烦。 - 易出错:手动管理 JNI 代码很容易导致内存泄漏或崩溃。
- 维护困难:代码结构复杂,难以维护。
NDK (Native Development Kit)
NDK 是一个工具集,它极大地简化了在 Android 上使用 C/C++ 的过程,它不仅仅包含 JNI,还提供了编译器、构建系统、调试工具等。
NDK 的核心优势:
- 简化开发:NDK 提供了更高级的 API(如
JNIHelper),让你可以用更现代的 C++ 风格进行开发,而不是手写繁琐的 JNI 代码。 - 构建系统:集成 CMake 或 ndk-build,你可以像在桌面端开发 C++ 项目一样管理原生代码的编译、链接,自动化程度高。
- 预构建库:NDK 自带了许多预编译好的库,如
libc++(C++ 标准库)、libandroid(Android 平台支持)、OpenSSL等,你只需在CMakeLists.txt中引用即可。 - ABI 支持:NDK 可以轻松为不同的 CPU 架构(如
armeabi-v7a,arm64-v8a,x86,x86_64)编译原生库,确保应用在不同设备上都能高效运行。 - C++ 标准:支持现代 C++ 标准(如 C++11, C++17),让你可以使用更强大、更安全的 C++ 特性。
现代 Android 开发中,几乎总是使用 NDK 来集成 C/C++ 代码,而不是直接手写 JNI。
实战:如何使用 Android Studio 和 NDK 集成 C++ 代码
这是目前最主流、最推荐的方式。
步骤 1:配置项目
-
创建一个新的 Android 项目。
-
在
build.gradle(Module: app) 文件中,启用viewBinding(可选) 并配置 NDK。// build.gradle (Module: app) android { // ... 其他配置 defaultConfig { // ... 其他配置 externalNativeBuild { cmake { // 可以指定 C++ 标准 cppFlags "-std=c++17" // 可以在这里传递参数给 CMake // arguments "-DANDROID_STL=c++_shared" } } // 指定需要支持的 CPU 架构 ndk { abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86' } } // 配置 CMake 构建文件的路径 externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" version "3.18.1" // 使用你安装的 CMake 版本 } } } dependencies { // ... 其他依赖 }
步骤 2:创建 C++ 代码
-
在 Android Studio 中,右键点击你的模块 -> New -> Folder -> CPP Folder,这会自动在
src/main/下创建一个cpp目录,并关联好CMakeLists.txt。 -
在
cpp目录下,创建一个 C++ 源文件,native-lib.cpp。// native-lib.cpp #include <jni.h> #include <string> // extern "C" 告诉编译器这段代码是 C 风格的,避免 C++ 的名称修饰 extern "C" JNIEXPORT jstring JNICALL Java_com_example_myapp_MainActivity_stringFromNative( JNIEnv* env, jobject /* this */, jstring inputString) { // 这里我们让 Java 传入一个字符串 // 将 Java 字符串转换为 C++ 字符串 const char* c_str = env->GetStringUTFChars(inputString, nullptr); std::string hello = "Hello from C++! You sent: "; hello += c_str; // 释放资源 env->ReleaseStringUTFChars(inputString, c_str); // 将 C++ 字符串转换回 Java 字符串并返回 return env->NewStringUTF(hello.c_str()); }Java_com_example_myapp_MainActivity_stringFromNative:这是 JNI 函数的命名规则。Java_: 固定前缀。com_example_myapp: 包名,用下划线_代替点 。MainActivity: 类名。stringFromNative: Java 中声明的native方法名。
- *`JNIEnv`**:指向 JNI 环境的指针,通过它可以调用 JNI API。
jobject:指向当前 Java 对象(这里是MainActivity)的引用。
步骤 3:配置 CMake (CMakeLists.txt)
在 cpp/CMakeLists.txt 文件中,添加你的原生库。
# CMakeLists.txt
# 设置 C++ 标准
cmake_minimum_required(VERSION 3.18.1)
# 定义项目名称
project("myapp")
# 添加一个原生共享库
add_library(
native-lib # 库名
SHARED # 共享库 (.so)
native-lib.cpp) # 源文件列表
# 查找并链接 Android 的日志库,方便在 C++ 中打印日志
find_library(
log-lib
log)
# 将你的库链接到 log-lib
target_link_libraries(
native-lib
${log-lib})
步骤 4:在 Java/Kotlin 中调用
-
在你的
MainActivity.java(或MainActivity.kt) 中声明native方法并加载库。// MainActivity.java package com.example.myapp; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; import android.widget.Button; public class MainActivity extends AppCompatActivity { // 声明一个 native 方法 public native String stringFromNative(String input); static { // 加载编译好的原生库 System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv = findViewById(R.id.sample_text); Button button = findViewById(R.id.my_button); button.setOnClickListener(v -> { // 调用 native 方法 String result = stringFromNative("Hello Android!"); tv.setText(result); }); } } -
在
res/layout/activity_main.xml中添加一个TextView和一个Button。
步骤 5:编译和运行
点击 Android Studio 的 "Run" 按钮,Android Studio 会自动:
- 调用 CMake 来编译
native-lib.cpp,生成.so文件。 - 将
.so文件打包到 APK 中。 - 运行 App,点击按钮,你会看到 TextView 中显示从 C++ 返回的字符串。
总结与最佳实践
| 特性 | JNI | NDK |
|---|---|---|
| 定位 | 一个编程接口/规范 | 一个完整的工具集 |
| 易用性 | 繁琐,需要大量手动胶水代码 | 简单,提供了高级 API 和构建系统 |
| 核心 | 手动实现 native 方法的 C/C++ 函数 |
集成 CMake,管理原生代码的编译和链接 |
| 适用场景 | 非常小的、一次性的性能优化;学习 JNI 原理 | 绝大多数场景,特别是集成现有库或开发复杂原生模块 |
| C++ 支持 | 较弱,容易出错 | 强大,支持现代 C++ 标准 |
最佳实践:
- 优先使用 NDK:除非你有非常特殊的需求,否则请始终使用 NDK 来管理你的 C/C++ 代码。
- CMake 是你的朋友:熟练掌握
CMakeLists.txt的编写,它能让你高效地管理复杂的原生项目。 - 封装与解耦:尽量将 C/C++ 代码封装成独立的、无状态的模块,通过定义清晰的接口(在 Java/Kotlin 端),减少 C++ 和 Java 之间的频繁交互,降低耦合度。
- 错误处理:原生代码的崩溃(如段错误)不会像 Java 异常那样被捕获,会导致整个 App 崩溃,务必做好原生代码的错误检查和日志记录。
- 调试:Android Studio 提供了强大的原生代码调试功能,你可以像调试 Java 代码一样设置断点、单步执行 C/C++ 代码,这对于排查问题至关重要。
虽然 C 语言不能独立开发一个完整的 Android App,但它是 Android 生态中不可或缺的一部分,通过 NDK,你可以将 C/C++ 的强大性能和丰富库生态无缝地集成到你的 Java/Kotlin 应用中,打造出既功能强大又性能卓越的移动应用。
