目录
- 什么是 JNI?
- 为什么在 Android 中使用 JNI?
- JNI 开发完整流程
- 在 Java/Kotlin 中声明
native方法 - 生成 JNI 头文件(
.h) - 实现 C/C++ 方法(
.c或.cpp) - 配置 CMake 构建脚本
- 加载 C/C++ 库并调用
- 在 Java/Kotlin 中声明
- 一个完整的实战示例
- 创建项目
- 编写 Java 代码
- 生成和编写 C++ 代码
- 配置 CMake
- 调用与测试
- JNI 常用数据类型和方法映射
- 进阶与最佳实践
- 处理字符串 (
jstring) - 异常处理
- 使用 C++ (C++风格的调用)
- 日志输出 (
__android_log_print)
- 处理字符串 (
什么是 JNI?
JNI (Java Native Interface) 是一个标准的编程接口,它允许运行在 Java 虚拟机 (JVM) 中的 Java 代码与其他语言(主要是 C 和 C++)编写的代码进行交互,它是一座连接 Java 世界和 C/C++ 世界的桥梁。

为什么在 Android 中使用 JNI?
在 Android 开发中,使用 JNI 主要有以下原因:
- 性能优化:对于计算密集型任务,如图形处理、物理模拟、音视频编解码等,用 C/C++ 实现比 Java/Kotlin 快得多。
- 访问硬件/系统 API:Android 系统的一些底层功能(如访问传感器原始数据、自定义驱动等)没有提供 Java API,只能通过 C/C++ 调用。
- 复用现有库:很多成熟的、高效的库都是用 C/C++ 编写的(OpenCV, FFmpeg),通过 JNI 可以直接在 Android 项目中使用它们,而无需重新编写。
- 保护代码:核心算法或逻辑放在 C/C++ 中,比在 Java/Kotlin 中更难被反编译和窃取。
JNI 开发完整流程
假设我们已经在 Android Studio 中创建了一个新的项目。
在 Java/Kotlin 中声明 native 方法
在 Java 或 Kotlin 类中声明一个或多个 native 方法,这些方法没有具体的实现,只是一个声明。
// 在 com.example.jnidemo 包下的 JniUtils.java 文件中
package com.example.jnidemo;
public class JniUtils {
// 1. 声明一个 native 方法
public native String stringFromJNI();
}
生成 JNI 头文件(.h)
头文件包含了 Java native 方法的 C/C++ 函数签名,你可以使用 javac 和 javah 工具(在旧版 Android Studio 中)或直接使用 Android Studio 的外部工具(推荐)来生成。

在较新的 Android Studio (CMake/NDK 集成) 中,最简单的方法是:
- 将光标放在
native方法声明上。 - 按
Alt + Enter(或Cmd + Enteron Mac)。 - 在弹出的菜单中选择 "Copy C++ Function Signature"。
- 你会得到类似
Java_com_example_jnidemo_JniUtils_stringFromJNI(JNIEnv *, jobject)的函数签名,这就是你在 C/C++ 中需要实现的函数名。
或者,你可以手动创建一个 .h 文件并包含以下内容:
// com_example_jnidemo_JniUtils.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_jnidemo_JniUtils */
#ifndef _Included_com_example_jnidemo_JniUtils
#define _Included_com_example_jnidemo_JniUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_jnidemo_JniUtils
* Method: stringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_jnidemo_JniUtils_stringFromJNI
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
函数名规则:Java_ + 包名(用下划线 _ 分隔) + 类名 + **方法名`。
实现 C/C++ 方法(.c 或 .cpp)
创建一个 C 或 C++ 源文件(native-lib.cpp),并实现你在头文件中定义的函数。

// native-lib.cpp
#include <jni.h>
#include <string>
#include <android/log.h> // 用于在 logcat 中打印日志
#define TAG "JniDemo"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
// 包含我们生成的头文件 (可选,但推荐)
// #include "com_example_jnidemo_JniUtils.h"
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jnidemo_JniUtils_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
// 创建一个 C++ 字符串
std::string hello = "Hello from C++";
// 将 C++ 字符串转换为 jstring (Java 字符串)
// env->NewStringUTF() 是一个 JNI 函数
return env->NewStringUTF(hello.c_str());
}
配置 CMake 构建脚本
这是最关键的一步,你需要告诉 Android 的构建系统如何编译你的 C/C++ 代码。
-
在
app模块的build.gradle文件中,确保ndkVersion和externalNativeBuild已配置:// app/build.gradle android { // ... 其他配置 defaultConfig { // ... 其他配置 externalNativeBuild { cmake { cppFlags "" // 可以指定 ABI 架构 // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } } ndkVersion "25.1.8937393" // 使用你安装的 NDK 版本 } // ... 其他配置 // 指定 CMakeLists.txt 文件的位置 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) # 定义你的 native 库 # SHARED 表示生成一个 .so (共享库) 文件 add_library( native-lib # 库的名字 SHARED # 库的类型 native-lib.cpp) # 源文件列表 # 找到 Android 的 log 库 find_library( log-lib log) # 将 log 库链接到你的 native-lib # 这样你就可以在 C++ 代码中使用 __android_log_print 了 target_link_libraries( native-lib ${log-lib})
加载 C/C++ 库并调用
在你的 Java/Kotlin 代码中加载库并调用 native 方法。
// 在你的 Activity 或其他地方
package com.example.jnidemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
// 加载名为 "native-lib" 的库
// 这个名字必须和 CMakeLists.txt 中 add_library 的第一个参数一致
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);
// 调用 native 方法
String hello = JniUtils.stringFromJNI();
tv.setText(hello);
// 测试一下 Toast
Toast.makeText(this, "Toast from Java!", Toast.LENGTH_SHORT).show();
}
}
运行你的 App,你应该能在屏幕上看到 "Hello from C++",并且在 Logcat 中能看到通过 __android_log_print 打印的日志。
