核心概念:JNI (Java Native Interface)
JNI 是 Java 平台的一部分,它定义了一套规范,允许 Java 代码和其他语言(如 C, C++, Assembly)进行交互,它是一座桥梁,连接着 Java 世界和原生代码世界。

为什么需要 JNI?
- 性能优化:对于图像处理、物理模拟、音视频编解码等计算密集型任务,C/C++ 的执行效率远高于 Java。
- 硬件交互:直接操作硬件(如 GPS、传感器、蓝牙)通常需要通过 C/C++ 驱动。
- 代码复用:利用现有的、成熟的 C/C++ 库(如 OpenSSL、FFmpeg)。
- 保护核心算法:将关键逻辑放在 C/C++ 中,比纯 Java 更难被逆向工程。
调用流程概览
整个过程可以分为以下几个步骤:
- 编写 Java 代码:在 Java 类中声明
native方法。 - 生成 C/C++ 头文件:使用
javac和javah(或新版 Android Studio 的自动工具)生成包含 C 函数原型的头文件。 - 实现 C/C++ 代码:创建
.c或.cpp文件,实现头文件中声明的函数。 - 构建原生库:将 C/C++ 代码编译成
.so(Shared Object) 文件。 - 打包并调用:将
.so文件打包到 APK 的正确目录下,并在 Java 代码中加载并调用。
详细步骤与代码示例
我们将创建一个简单的示例:Java 调用一个 C 函数,该函数接收两个整数并返回它们的和。
步骤 1: 创建 Android 项目并编写 Java 代码
- 在 Android Studio 中创建一个新的项目。
- 创建一个 Java 类,
com.example.jnidemo.NativeUtils。
// NativeUtils.java
package com.example.jnidemo;
public class NativeUtils {
// 1. 声明一个 native 方法
// 注意:方法名需要遵循特定的命名规则:Java_包名_类名_方法名
public native int add(int a, int b);
// 2. 加载原生库
// System.loadLibrary 会在库的路径下寻找 "lib库名.so"
// 所以这里的库名是 "jnidemo",对应的库文件是 "libjnidemo.so"
static {
System.loadLibrary("jnidemo");
}
}
步骤 2: 生成 C/C++ 头文件 (自动生成)
在较新版本的 Android Studio 中,这个过程是自动化的。

- 将光标放在
add(int a, int b)方法上。 - 按
Alt + Enter(或Option + Enter)。 - 在弹出的菜单中选择 "Create function..." 或 "Create implementation..."。
- 在接下来的对话框中,选择 "C/C++ Header"。
- Android Studio 会在
app/src/main/cpp/目录下自动创建一个NativeUtils.h文件。
生成的 NativeUtils.h 文件内容如下:
// NativeUtils.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_jnidemo_NativeUtils */
#ifndef _Included_com_example_jnidemo_NativeUtils
#define _Included_com_example_jnidemo_NativeUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_jnidemo_NativeUtils
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_jnidemo_NativeUtils_add
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
关键点解析:
#include <jni.h>:包含了 JNI 的所有核心定义和数据类型。JNIEXPORT和JNICALL:是 JNI 的关键字,用于指定函数的调用规范。jint:JNI 对应 Javaint类型的数据类型。JNIEnv *:一个指向 JNI 环境的指针,通过这个指针,你可以在 C 代码中调用 Java 的方法、操作 Java 对象等。jobject:代表调用这个 native 方法的 Java 对象实例(如果是静态方法,则为jobject代表Class对象)。Java_com_example_jnidemo_NativeUtils_add:这就是 Java 和 C 函数的绑定名称,规则是Java_完整类名_方法名。
步骤 3: 实现 C/C++ 代码
在 app/src/main/cpp/ 目录下创建一个 native-lib.cpp 文件(如果不存在),并实现 add 函数。
// native-lib.cpp
#include <jni.h>
#include <string>
// 实现 Java_com_example_jnidemo_NativeUtils_add 函数
extern "C" JNIEXPORT jint JNICALL
Java_com_example_jnidemo_NativeUtils_add(
JNIEnv* env,
jobject /* this */,
jint a,
jint b) {
// 直接返回两个数的和
return a + b;
}
步骤 4: 配置 CMake 构建脚本
这是将 C/C++ 代码编译成 .so 文件的关键配置。

- 打开
app/build.gradle文件,确保externalNativeBuild已配置,并指向CMakeLists.txt。
// app/build.gradle
android {
// ...
defaultConfig {
// ...
externalNativeBuild {
cmake {
cppFlags ""
// 可以指定支持的 ABI 架构
// abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
}
// ...
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.18.1" // 使用你安装的 CMake 版本
}
}
}
- 确保
app/src/main/cpp/CMakeLists.txt文件存在并配置正确。
# CMakeLists.txt
# 设置 C++ 标准
cmake_minimum_required(VERSION 3.4.1)
# 定义库名,必须与 Java 代码中 System.loadLibrary("库名") 一致
add_library( # 设置库的名称
jnidemo # 库名
# 设置库的类型
SHARED # 共享库,即 .so 文件
# 提供源文件的相对路径
native-lib.cpp )
# 找到指定的本机平台库
find_library( # 设置路径变量的名称
log-lib
# 指定NDK库名称
log )
# 将库链接到目标库
target_link_libraries( # 指定目标库
jnidemo
# 链接要包含的库
${log-lib} )
步骤 5: 在 Java 中调用并测试
现在回到你的 Activity(MainActivity.java),加载库并调用 add 方法。
// MainActivity.java
package com.example.jnidemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 找到 TextView 用于显示结果
TextView tv = findViewById(R.id.sample_text);
// 创建 NativeUtils 对象并调用 native 方法
NativeUtils nativeUtils = new NativeUtils();
int result = nativeUtils.add(10, 20);
// 在 TextView 中显示结果
tv.setText("10 + 20 = " + result);
}
}
运行 App,你将在屏幕上看到 "10 + 20 = 30"。
进阶:数据类型传递与回调
传递和返回字符串
Java 代码:
// NativeUtils.java public native String sayHello();
C++ 代码:
// native-lib.cpp
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jnidemo_NativeUtils_sayHello(
JNIEnv* env,
jobject /* this */) {
// 使用 env->NewStringUTF 创建一个 Java 字符串
return env->NewStringUTF("Hello from C++!");
}
传递 Java 对象并修改其字段
假设我们有一个 Person 类。
Person.java:
public class Person {
public String name;
public int age;
}
Java 代码:
// NativeUtils.java public native void updatePerson(Person person);
C++ 代码:
// native-lib.cpp
#include <jni.h>
extern "C" JNIEXPORT void JNICALL
Java_com_example_jnidemo_NativeUtils_updatePerson(
JNIEnv* env,
jobject /* this */,
jobject person) { // 接收一个 Java 对象
// 1. 获取 Person 类的 class 对象
jclass personClass = env->GetObjectClass(person);
// 2. 获取字段 ID
jfieldID nameFieldID = env->GetFieldID(personClass, "name", "Ljava/lang/String;");
jfieldID ageFieldID = env->GetFieldID(personClass, "age", "I");
// 3. 修改字段
env->SetObjectField(person, nameFieldID, env->NewStringUTF("Alice"));
env->SetIntField(person, ageFieldID, 30);
}
Java 中调用:
Person person = new Person(); person.name = "Bob"; person.age = 25; nativeUtils.updatePerson(person); // person.name 变为 "Alice", person.age 变为 30
重要注意事项与最佳实践
- 线程问题:原生代码不是线程安全的! 你只能在创建该线程的 JNI 接口上调用 JNI 函数,从主线程调用的 native 方法,其 C 代码也只能在主线程执行,耗时操作必须在新线程中处理。
- 性能开销:Java 和 C 之间的数据传递(尤其是对象和数组)是有性能开销的,应尽量减少频繁的跨语言调用,一次性传递大量数据比多次传递少量数据更高效。
- 内存管理:
- C 分配的内存:如果在 C 中使用
malloc分配了内存,必须用free释放,否则会导致内存泄漏。 - JNI 引用:
JNIEnv提供的NewGlobalRef,NewLocalRef等创建的 JNI 引用,如果不手动释放,会导致内存泄漏,全局引用需要DeleteGlobalRef,局部引用在函数返回时自动释放,但在循环或长期运行的代码中需要手动DeleteLocalRef。
- C 分配的内存:如果在 C 中使用
- 错误处理:C/C++ 代码中的错误(如空指针、除零)会导致 JVM 崩溃(
SIGSEGV),必须在 C 代码中进行充分的检查和处理。 - ABIs (Application Binary Interfaces):不同的 CPU 架构(如
arm64-v8a,armeabi-v7a,x86_64)需要对应编译的.so文件,Android Studio 默认会为所有支持的 ABI 编译,APK 安装时,系统会自动选择合适的.so文件,为了减小 APK 大小,你可以通过abiFilters只打包你需要的架构。 - 现代替代方案:
- Kotlin/Native:Kotlin 官方提供的解决方案,可以编译成原生二进制文件,与 Java/Kotlin 交互比 JNI 更简单、更安全。
- Runtime.exec():如果只是想调用一个外部的命令行工具,可以使用
Runtime.getRuntime().exec(),但这比 JNI 要重,且进程间通信更复杂。
希望这份详细的指南能帮助你掌握在 Android 中使用 Java 调用 C/C++ 的技能!
