与常规的 Java/Kotlin 开发不同,C 开发不用于构建应用的 UI 界面,而是专注于高性能计算、代码复用、硬件访问和算法优化等领域。

核心概念:为什么要在 Android 上用 C?
在开始之前,你必须明白 NDK 的定位:它是 Java/Kotlin 的补充,而不是替代品。
-
Java/Kotlin (ART - Android Runtime):
- 优点: 跨平台、内存管理自动、开发效率高、拥有所有 Android SDK 的 API。
- 缺点: 性能受限于 JIT/AOT 解释和垃圾回收,无法直接访问底层硬件和操作系统特性。
-
C/C++ (Native Code):
- 优点:
- 极致性能: 代码被编译成机器码,直接在 CPU 上运行,没有虚拟机开销。
- 硬件访问: 可以直接调用 Linux 内核 API,访问摄像头传感器、GPS、OpenGL ES 等。
- 代码复用: 可以将现有的 C/C++ 库(如游戏引擎、音视频编解码库)直接集成到 Android 应用中。
- 多线程: 可以创建不受 Dalvik/ART 虚拟机管理的原生线程,适合计算密集型任务。
- 缺点:
- 开发复杂: 没有自动内存管理,需要手动管理内存,容易引发崩溃。
- 平台相关: 需要为不同的 CPU 架构(ARM, x86)分别编译。
- 调试困难: 崩溃时可能只看到一个 native crash 的地址,难以定位。
- API 有限: 不能直接调用 Android Framework 的 Java API。
- 优点:
环境准备
在开始之前,请确保你的开发环境已经配置好。

- 安装 Android Studio: 这是最主要的开发工具。
- 安装 NDK 和 CMake:
- 打开 Android Studio。
- 进入
Tools->SDK Manager。 - 切换到
SDK Tools标签页。 - 勾选
NDK (Side by side)和CMake。 - 点击
Apply或OK进行安装。
开发流程:从零开始创建一个 C 库并调用它
我们将通过一个最经典的例子:在 Java 中调用 C 代码实现两个整数相加。
步骤 1:创建一个支持 C/C++ 的新项目
- 打开 Android Studio,选择
File->New->New Project...。 - 选择
Native C++模板(这是最简单的方式,它会自动帮你配置好所有东西),点击Next。 - 输入你的应用名称(
MyCApp),选择包名和保存位置,点击Next。 - 选择 C++ 标准(C++17 是一个不错的选择),点击
Finish。 - Android Studio 会为你创建一个项目,并包含一个示例的 native-lib.cpp 文件。
步骤 2:理解项目结构
创建项目后,你会发现几个关键的变化:
app/src/main/cpp/: 这是存放你的 C/C++ 源代码的地方。native-lib.cpp: 你的第一个 C++ 文件,这里已经有一个示例函数stringFromJNI()。
app/src/main/java/com/yourcompany/mycapp/: Java/Kotlin 代码。MainActivity.java: 包含加载库和调用 native 方法的代码。
CMakeLists.txt: 这是 C/C++ 的构建脚本,它告诉 CMake 如何编译你的 native 代码,生成一个共享库(.so文件)。build.gradle (Module: app): 这个文件被修改,包含了 NDK 和 CMake 的路径配置。
步骤 3:编写 C/C++ 代码
打开 app/src/main/cpp/native-lib.cpp,我们将修改它。
#include <jni.h>
#include <string>
// extern "C" 是必须的,它告诉 C++ 编译器使用 C 语言的链接方式,
// 这样 Java 才能通过函数名找到这个函数。
extern "C" JNIEXPORT jstring JNICALL
Java_com_yourcompany_mycapp_MainActivity_addNumbers(
JNIEnv* env,
jobject /* this */,
jint a,
jint b) {
// 执行加法运算
int result = a + b;
// 将结果转换成字符串
std::string result_str = "The result is: " + std::to_string(result);
// JNI 函数,将 C++ 的 std::string 转换成 Java 的 String 对象
return env->NewStringUTF(result_str.c_str());
}
代码解释:

extern "C": 这是 JNI 的关键,Java 调用 native 方法时,会查找一个特定的函数名。extern "C"确保了函数名不会被 C++ 的名称修饰(Name Mangling)所改变。JNIEXPORT/JNICALL: 这是 JNI 的宏定义,表示该函数可以被外部调用。Java_com_yourcompany_mycapp_MainActivity_addNumbers:- 这是 JNI 的函数命名规则:
Java_+ 包名(用下划线_替换点 ) +_+ 类名 +_+ 方法名。 MainActivity会调用这个函数。
- 这是 JNI 的函数命名规则:
JNIEnv* env: 指向 JNI 环境的指针,通过它你可以调用 JNI 函数来操作 Java 对象。jobject this: 指向调用该 native 方法的 Java 对象(这里是MainActivity的实例)。jint a, jint b:jint是 JNI 中对 Javaint类型的映射,这两个参数来自 Java 方法。
步骤 4:修改 CMakeLists.txt
这个文件告诉 CMake 如何编译你的代码,默认情况下,它已经包含了你的 native-lib.cpp,如果你添加了新的 .cpp 文件,需要在这里声明。
打开 app/CMakeLists.txt,你会看到类似这样的内容:
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.18.1)
# project 定义项目名称和 C++ 标准
project("mycapp")
# 添加一个库
# add_library(库名称 SHARED 源文件列表)
add_library(
native-lib
SHARED
native-lib.cpp)
# 找到 Android 的日志库
find_library(
log-lib
log)
# 将库链接到你的 native-lib
# target_link_libraries(库名称 要链接的库)
target_link_libraries(
native-lib
${log-lib})
这里 add_library 是核心,它定义了一个名为 native-lib 的共享库,其源文件是 native-lib.cpp。
步骤 5:在 Java/Kotlin 中调用 C 代码
我们需要在 MainActivity.java 中声明 native 方法,并加载库。
打开 app/src/main/java/com/yourcompany/mycapp/MainActivity.java。
package com.yourcompany.mycapp;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Button;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
// 加载名为 "native-lib" 的库
// 这个库名必须和 CMakeLists.txt 中 add_library 的第一个参数一致
static {
System.loadLibrary("native-lib");
}
// 声明一个 native 方法
// 函数签名必须和 C++ 中的函数名完全匹配(去掉 Java_ 前缀)
// 参数和返回类型使用 JNI 类型
public native String addNumbers(int a, int b);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取 UI 控件
TextView tv = findViewById(R.id.sample_text);
Button button = findViewById(R.id.my_button);
// 设置按钮点击事件
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 调用 C 函数
String result = addNumbers(10, 25);
// 显示结果
tv.setText(result);
Toast.makeText(MainActivity.this, "C code called!", Toast.LENGTH_SHORT).show();
}
});
}
}
代码解释:
System.loadLibrary("native-lib"): 这行代码会去加载libnative-lib.so这个库,这个.so文件是由 CMake 在编译时生成的。public native String addNumbers(int a, int b);: 这就是 Java 声明。native关键字告诉 JVM 这个方法的实现不在 Java 中,而是在本地代码里,参数和返回类型与 C++ 函数对应。addNumbers(10, 25): 像调用普通 Java 方法一样调用它,参数会自动转换成 JNI 类型传递给 C 函数,C 函数的返回值也会自动转换成 Java 的String。
步骤 6:运行应用
- 确保你的模拟器或真机已连接。
- 点击 Android Studio 的绿色 "Run" 按钮。
- 应用启动后,点击按钮,你会看到文本区域显示 "The result is: 35",同时会弹出一个 Toast 提示。
恭喜!你已经成功地在 Android 应用中调用了 C 语言代码!
C 开发进阶与资源
数据类型映射
Java 和 C/C++ 之间的数据类型需要通过 JNI 进行转换。
| Java 类型 | JNI 类型 | C/C++ 类型 |
|---|---|---|
byte |
jbyte |
signed char |
short |
jshort |
short |
int |
jint |
int |
long |
jlong |
long long |
float |
jfloat |
float |
double |
jdouble |
double |
char |
jchar |
unsigned short |
boolean |
jboolean |
unsigned char (true=1, false=0) |
String |
jstring |
- |
Object |
jobject |
- |
Class |
jclass |
- |
调试 Native 代码
调试 C/C++ 代码比调试 Java 困难,但 Android Studio 提供了强大的支持。
- 在 C/C++ 代码的行号左侧单击,设置断点(红点)。
- 在运行配置中,确保
Debug按钮旁边选择了native。 - 点击 "Debug" 按钮。
- 程序会停在断点处,你可以查看变量、单步执行等。
推荐学习资源
-
官方文档 (必读):
- Android NDK 官方文档: 最权威、最准确的信息来源。
- JNI 官方文档: 详细介绍了 JNI 的所有 API 和最佳实践。
-
书籍:
- 《Android NDK Beginner's Guide》: 非常适合入门,讲解清晰。
- 《The Android Developer's Cookbook》: 其中章节也涉及 NDK 开发。
-
在线教程与博客:
- ProAndroidDev (Medium 专栏): 经常有高质量的 NDK 相关文章。
- Stack Overflow: 解决具体问题的最佳去处,搜索时记得加上
android-ndk
-
GitHub 开源项目:
- FFmpeg Android: 学习如何集成和编译复杂的 C 库。
- OpenCV Android: 学习如何进行高性能的图像处理。
- 各种游戏引擎的源码: 如 Godot, Cocos2d-x 等,它们大量使用 NDK。
使用 C 开发 Android 应用是一项专业技能,它将你带入了更底层的世界,虽然入门比 Java/Kotlin 难,但它在游戏、音视频、AR/VR 等领域是不可或缺的。
学习路径建议:
- 掌握 JNI: 理解 Java 和 C 之间如何交互、数据如何传递、如何处理异常。
- 精通 CMake: 学会如何组织和管理你的 C/C++ 项目。
- 学习 Android NDK 特有的 API: 如传感器访问、OpenGL ES 绑定等。
- 实践: 从集成一个简单的第三方库开始,
zlib,逐步挑战更复杂的项目。
祝你学习顺利!
