- 准备环境:配置好 Android Studio 和 NDK。
- 编写 Java/Kotlin 代码:声明
native方法。 - 生成 JNI 头文件:使用
javah或 Android Studio 的工具生成 C/C++ 的函数声明。 - 实现 C/C++ 代码:编写具体的 native 方法实现。
- 配置 CMake/NDK:告诉编译系统如何编译你的 C/C++ 代码。
- 加载库并调用:在 Java 代码中加载库并调用
native方法。
详细步骤与示例
我们将创建一个简单的示例:Java 传递两个整数给 C/C++,C/C++ 计算它们的和并返回给 Java。

步骤 1: 准备环境
-
安装 NDK 和 CMake:
- 打开 Android Studio。
- 进入
Tools->SDK Manager。 - 在
SDK Platforms标签页,确保你需要的 Android API 已安装。 - 在
SDK Tools标签页,勾选NDK (Side by side)和CMake,然后点击Apply安装。
-
创建或打开一个项目:
- 创建一个新的 Android 项目,或者打开一个已有的项目。
- 在
build.gradle(Module: app) 文件中,确保android块包含了 NDK 和 CMake 的配置,如果新建项目时勾选了 "Include C++ support",这些配置会自动生成。
// build.gradle (Module: app) android { // ... 其他配置 defaultConfig { // ... 其他配置 externalNativeBuild { cmake { cppFlags "" } } // 指定要支持的 ABI 架构,这样只会编译对应的库,减小 APK 大小 ndk { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } } externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" version "3.18.1" // 使用你安装的 CMake 版本 } } }
步骤 2: 编写 Java/Kotlin 代码
在你的 Java 或 Kotlin 文件中(MainActivity.java),声明一个或多个 native 方法,这些方法只是声明,没有实现。
// app/src/main/java/com/example/myapplication/MainActivity.java
package com.example.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
// 声明一个 native 方法
// 注意:方法名必须遵循特定的规则,我们稍后会讲到
public native int add(int a, int b);
static {
// 加载我们即将创建的 native 库
// "native-lib" 是我们在 CMakeLists.txt 中定义的库的名称
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);
int result = add(10, 20); // 调用 native 方法
tv.setText("The result from C++ is: " + result);
}
}
关键点:

public native int add(int a, b);:native关键字告诉 JVM 这个方法的实现在别处(C/C++)。static { System.loadLibrary("native-lib"); }:在类加载时执行,用于加载我们的动态链接库(.so文件),库名native-lib必须与 CMakeLists.txt 中定义的target_name完全一致。
步骤 3: 生成 JNI 头文件
这一步的目的是让 C/C++ 编译器知道 add 函数的“长相”(即函数签名)。
在较新的 Android Studio 中,这个过程可以自动化,但了解手动步骤有助于理解原理。
手动方法 (使用 javah):
-
先编译你的 Java 代码,生成
class文件。
(图片来源网络,侵删) -
打开 Android Studio 的 Terminal (
View->Tool Windows->Terminal)。 -
执行
javah命令,你需要指定包含native方法的类的完整包路径和.class文件所在的目录。# 假设你的 .class 文件在 build/intermediates/javac/debug/classes/ 目录下 # 你的类是 com.example.myapplication.MainActivity javah -d jni -cp build/intermediates/javac/debug/classes com.example.myapplication.MainActivity
-d jni:将生成的头文件保存到jni目录(如果不存在会自动创建)。-cp ...:指定.class文件的类路径。com.example.myapplication.MainActivity:包含native方法的完整类名。
执行后,你会在 app/src/main/jni/ 目录下找到一个名为 com_example_myapplication_MainActivity.h 的头文件。
自动方法 (推荐):
现代 Android Studio 通常会自动处理这个过程,当你配置好 CMake 并添加了 native 方法后,当你点击 "Build" -> "Make Project" 或 "Rebuild Project" 时,AS 会自动生成所需的头文件,通常位于 app/build/intermediates/cmake/debug/obj/armeabi-v7a/include/ 目录下,你可以手动复制到 app/src/main/cpp/ 目录下。
步骤 4: 实现 C/C++ 代码
我们来实现 add 函数。
- 在
app/src/main/cpp/目录下,将上一步生成的头文件(com_example_myapplication_MainActivity.h)复制过来。 - 创建一个新的 C++ 源文件,
native-lib.cpp。
头文件 (com_example_myapplication_MainActivity.h) 的内容:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_myapplication_MainActivity */
#ifndef _Included_com_example_myapplication_MainActivity
#define _Included_com_example_myapplication_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_myapplication_MainActivity
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_myapplication_MainActivity_add
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
关键点:
JNIEXPORT和JNICALL:这是 JNI 规定的宏,用于标识函数的导出方式。Java_+ 完整类名(下划线分隔) + 方法名:这就是 Javanative方法在 C/C++ 中的映射规则。com.example.myapplication.MainActivity->com_example_myapplication_MainActivityadd->add- 组合起来就是
Java_com_example_myapplication_MainActivity_add。
JNIEnv *:指向 JNI 环境的指针,通过它可以调用 JNI 提供的各种函数(例如创建对象、访问数组等)。jobject:指向调用该 native 方法的 Java 对象(这里是MainActivity的实例)的指针,如果是static native方法,这个参数会是jobject(指向Class对象)。(II)I:这是 JNI 方法签名。- 括号内是参数类型。
- 括号外是返回类型。
I代表int。II代表两个int参数。I返回一个int。
C++ 实现文件 (native-lib.cpp):
#include "com_example_myapplication_MainActivity.h"
// 实现 add 方法
JNIEXPORT jint JNICALL Java_com_example_myapplication_MainActivity_add(JNIEnv *env, jobject /* this */, jint a, jint b) {
// 简单的加法运算
return a + b;
}
注意:第二个参数 jobject this 在这个例子中没有用到,我们将其命名为 /* this */ 并注释掉,以避免编译器警告。
步骤 5: 配置 CMake
配置 CMakeLists.txt 文件,告诉 CMake 如何编译你的 C++ 代码。
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.18.1)
# 定义项目名称
project("myapplication")
# 添加一个库
# SHARED 表示生成一个动态链接库 (.so 文件)
# native-lib 是我们给库起的名字,必须与 System.loadLibrary("native-lib") 中的名字一致
add_library(
native-lib
SHARED
native-lib.cpp)
# 查找 Android 的日志库
find_library(
log-lib
log)
# 将日志库链接到我们的 native-lib
# 这样我们就可以在 C/C++ 代码中使用 __android_log_print 来打印日志
target_link_libraries(
native-lib
${log-lib})
步骤 6: 运行和调试
你已经完成了所有配置,点击 Android Studio 的 "Run" 按钮,将应用安装到模拟器或真机上。
当 MainActivity 启动时,它会执行 onCreate 方法,调用 add(10, 20),这个调用会通过 JNI 机制,执行 native-lib.cpp 中的 Java_com_example_myapplication_MainActivity_add 函数,计算 10 + 20 = 30,然后将结果 30 返回给 Java 代码,屏幕上会显示 "The result from C++ is: 30"。
JNI 数据类型映射
Java 和 C/C++ 的数据类型不是一一对应的,下表是常用的映射关系:
| Java 类型 | JNI 类型 | C/C++ 类型 | 描述 |
|---|---|---|---|
byte |
jbyte |
signed char |
8位有符号整数 |
short |
jshort |
short |
16位有符号整数 |
int |
jint |
int |
32位有符号整数 |
long |
jlong |
long long |
64位有符号整数 |
float |
jfloat |
float |
32位浮点数 |
double |
jdouble |
double |
64位浮点数 |
char |
jchar |
unsigned short |
16位 Unicode 字符 |
boolean |
jboolean |
unsigned char |
8位布尔值 (0=false, 1=true) |
Object |
jobject |
void * |
任何 Java 对象 |
Class |
jclass |
void * |
Java Class 对象 |
String |
jstring |
void * |
Java String 对象 |
int[] |
jintArray |
void * |
Java int 数组 |
| ... | ... | ... | ... |
调用 JNI 的核心流程可以概括为:
- Java 声明:用
native关键字定义方法。 - 加载库:用
System.loadLibrary()加载编译好的.so文件。 - 生成签名:根据 Java 方法的包名、方法名和参数列表,生成 C/C++ 函数名
Java_...。 - C/C++ 实现:按照 JNI 规则实现函数,接收
JNIEnv*和jobject等参数,并进行类型映射。 - 编译配置:通过 CMake 将 C/C++ 源文件编译成动态库。
掌握这个流程后,你就可以将计算密集型、需要与硬件交互或复用现有 C/C++ 库的逻辑放到 native 层,从而提升 Android 应用的性能和功能。
